2026-07-20 09:49:31 +00:00
meh
2026-07-14 20:19:21 +00:00
2026-07-20 09:49:31 +00:00
v4
2026-07-08 07:52:34 +00:00
meh
2026-07-18 15:48:29 +00:00
meh
2026-07-14 20:19:21 +00:00
2026-07-16 12:36:22 +00:00
2026-06-21 14:17:24 +00:00
2026-07-20 09:49:31 +00:00
2026-07-12 19:27:47 +00:00
2026-07-12 19:27:47 +00:00
2026-07-08 09:08:17 +00:00
meh
2026-07-14 20:19:21 +00:00
2026-07-16 12:36:22 +00:00

Profen Engineering — React SPA

A React single-page application that recreates profeneng.com (WordPress + Elementor + Rakar theme) as a fast, portable frontend.

Architecture

src/
├── main.jsx                  Entry — imports CSS (variables → kit → theme → layout → pages → services → dynamic), mounts React
├── App.jsx                   Router (BrowserRouter, Suspense, ErrorBoundary, lazy-loaded pages, 404 route)
├── config.js                 Central config: WhatsApp phone, brand colors, PAGE_IDS, SPA_ROUTES, POST_ID_TO_SLUG, SERVICE_SIDEBAR_ITEMS, MENU_ITEM_IDS
├── components/
│   ├── Layout.jsx            Shared layout (Header + Outlet + Footer + WhatsAppButton + preloader + pageid body class)
│   ├── SafeHtml.jsx          Parses HTML strings into React elements via html-react-parser (replaces dangerouslySetInnerHTML)
│   ├── ErrorBoundary.jsx     Catches render crashes, shows Try Again button
│   ├── Loading.jsx           Spinner for Suspense fallback
│   ├── WhatsAppButton.jsx    Floating WhatsApp widget
│   └── sections/
│       ├── Header.jsx        Proper React component with <Link> nav, active state from useLocation()
│       ├── Footer.jsx        Proper React component with contact data, social links, service links
│       ├── Breadcrumb.jsx    Reusable breadcrumb trail
│       ├── SectionTitle.jsx  Section heading with subtitle & icon
│       ├── Button.jsx        Reusable styled button
│       ├── PhoneWidget.jsx   Phone call widget
│       ├── FeatureCards.jsx  Feature card grid
│       ├── ContactForm.jsx   "Get a Quote" form
│       ├── ContactInfo.jsx   Address/phone/email display
│       ├── GoogleMap.jsx     Embedded Google Maps iframe
│       ├── Divider.jsx       Section divider
│       ├── ProjectFilterGrid.jsx  Filterable project grid
│       ├── ServiceContentBlocks.jsx  Renders structured service content (paragraphs, headings, lists)
│       ├── ServiceGridSection.jsx   Service listing grid
│       ├── ServiceBreadcrumb.jsx    Service detail breadcrumb
│       ├── ServiceImage.jsx         Service detail featured image
│       ├── ServiceBody.jsx          Service detail body (content blocks)
│       └── ServiceSidebar.jsx       Service detail sidebar nav
├── pages/
│   ├── Home.jsx              Page: /  — uses SafeHtml (parses home-content.html)
│   ├── About.jsx             Page: /about — uses SafeHtml (parses about-content.html)
│   ├── Services.jsx          Page: /service — uses SafeHtml (parses service-content.html)
│   ├── ServiceDetail.jsx     Page: /service/:slug — fully componentized, reads services.json
│   ├── Project.jsx           Page: /project — fully componentized, uses ProjectFilterGrid
│   └── Contact.jsx           Page: /contact — fully componentized, uses section components
├── hooks/
│   ├── useLayoutInit.js      Re-initializes Elementor theme JS on route change (requestAnimationFrame polling)
│   ├── useNavFix.js          Intercepts links inside a ref for proper SPA routing
│   └── useThemeAssets.js     Injects <meta> and <link> tags (OG, canonical, favicon) on mount
├── data/                     Content sources
│   ├── home-content.html     Home page HTML content (parsed by SafeHtml)
│   ├── about-content.html    About page HTML content (parsed by SafeHtml)
│   ├── service-content.html  Services listing HTML content (parsed by SafeHtml)
│   └── services.json         Structured data for 9 service detail pages (contentBlocks, images, metadata)
└── css/                      Build-time CSS imports (all bundled into one index-*.css)
    ├── variables.css         :root CSS custom properties for brand colors (imported first)
    ├── kit.css               Elementor kit CSS (global styles)
    ├── theme.css             Rakar theme overrides
    ├── layout.css            Header (elementor-945) + Footer (elementor-1098 / elementor-128) CSS
    ├── pages.css             Combined CSS for all 5 main pages
    ├── services.css          Combined CSS for all 9 service detail pages
    ├── dynamic.css           Dynamic overrides using var(--brand-*) with fallbacks
    └── pages/                Individual page CSS files (development reference — edit these, then regenerate combined)
        ├── page-23.css       Home page
        ├── page-31.css       About page
        ├── page-32.css       Services listing
        ├── page-36.css       Project page
        ├── page-48.css       Contact page
        └── service-*.css     Individual service detail pages (9 files)

Strategy: Hybrid React + WordPress Content

The app uses a hybrid approach:

  1. Componentized pages (Contact, Project, ServiceDetail) use proper React section components with structured data — no HTML strings.
  2. Transitional pages (Home, About, Services) render raw Elementor HTML from src/data/*.html files, parsed at runtime by html-react-parser via SafeHtml.jsxno dangerouslySetInnerHTML anywhere in the app.
  3. Styling comes from two places:
    • Build-time CSS imports (src/css/*.css) — Elementor's page-specific CSS bundled by Vite
    • Runtime assets from public/wp-content/ — theme stylesheets, fonts, images
  4. Behavior comes from the original Rakar theme JS loaded via <script> tags in index.html
  5. Theme re-initialization on route change is handled by useLayoutInit.js via requestAnimationFrame polling

Why This Works

Elementor pages are self-contained: each page has a unique CSS class (.elementor-<postId>) and the HTML references it. By loading ALL page CSS at once and the correct page HTML per route, we get pixel-perfect WordPress output without running PHP.

Migration Path

The codebase is in active migration from "raw Elementor HTML" to "proper React components":

Page Approach Status
Contact Full React components (ContactForm, ContactInfo, GoogleMap) Complete
Project Full React components (ProjectFilterGrid) Complete
ServiceDetail Structured data from services.json + React components Complete
Home SafeHtml parsing home-content.html 🔄 Transitional
About SafeHtml parsing about-content.html 🔄 Transitional
Services SafeHtml parsing service-content.html 🔄 Transitional

Section Components & Data Flow

Componentized Pages (Contact, Project, ServiceDetail)

These pages compose reusable section components rather than rendering raw HTML:

Contact.jsx
  ├── Breadcrumb          props: title, items[]
  ├── SectionTitle        props: subtitle, heading, alignment
  ├── ContactInfo         props: items[]  ({ icon, label, value, link })
  ├── ContactForm         props: heading
  ├── GoogleMap           props: src, title
  └── Divider

ServiceDetail.jsx
  ├── ServiceBreadcrumb   props: title
  ├── ServiceImage        props: src, srcSet, width, height
  ├── ServiceBody         props: blocks[]  (from services.json → contentBlocks)
  └── ServiceSidebar      props: activeSlug  (renders SERVICE_SIDEBAR_ITEMS from config.js)

Project.jsx
  └── ProjectFilterGrid   (self-contained, reads project data from public/wp-content/)

All section components return null/empty gracefully when props are missing — no hard crashes.

SafeHtml Pages (Home, About, Services)

Home.jsx
  └── SafeHtml
        └── html-react-parser(require('home-content.html'))
              └── useNavFix(ref) — intercepts <a> clicks for SPA navigation

About.jsx  (same pattern)
Services.jsx  (same pattern)

Central Configuration (config.js)

All magic values live in one file:

  • Brand colors: BRAND_PRIMARY, BRAND_DARK, BRAND_NAVY (from .env or defaults)
  • WhatsApp: WHATSAPP_PHONE (from .env)
  • Routes: SPA_ROUTES, PAGE_IDS (path → WordPress post ID)
  • Services: POST_ID_TO_SLUG, SERVICE_SIDEBAR_ITEMS, MENU_ITEM_IDS

Key Fixes & Workarounds

Issue Solution
Elementor lazyload hides background images Removed .e-con.e-parent:nth-of-type(n+4):not(.e-lazyloaded) CSS that applies background-image: none !important (was in theme.css — already cleaned)
Elfsight WhatsApp plugin crashes page Replaced with custom WhatsAppButton.jsx component
Theme JS not found on route change useLayoutInit.js polls with requestAnimationFrame until window.rakar_content_load_scripts is available
SPA links reload page useNavFix.js intercepts same-site links, uses react-router-dom's useNavigate; Header nav uses <Link> components
Missing Elementor JS dependencies Added webpack.runtime.min.js and frontend-modules.min.js to index.html
Waypoint plugin 404 Created empty shim at public/wp-includes/js/waypoint.min.js

Style Organization

CSS Import Order (in main.jsx)

import './css/variables.css'   // 1. Brand color variables (first so everything else can use them)
import './css/kit.css'         // 2. Elementor kit global styles
import './css/theme.css'       // 3. Rakar theme overrides
import './css/layout.css'      // 4. Header + Footer layout
import './css/pages.css'       // 5. Main page styles
import './css/services.css'    // 6. Service detail styles
import './css/dynamic.css'     // 7. Dynamic overrides using var(--brand-*) with fallbacks

Order mattersvariables.css is first so dynamic.css (which uses var(--brand-*)) resolves correctly.

Brand Color System

Colors are defined once in .env, flow through config.js, and land in CSS custom properties:

.env                    →  config.js          →  variables.css
VITE_BRAND_PRIMARY      →  BRAND_PRIMARY      →  --brand-primary
VITE_BRAND_DARK         →  BRAND_DARK         →  --brand-dark
VITE_BRAND_NAVY         →  BRAND_NAVY         →  --brand-navy

dynamic.css uses these variables with fallbacks (e.g., var(--brand-primary, #FA2D39)) so it works even if variables.css is somehow absent.

Regenerating Combined CSS

After editing individual page files in src/css/pages/:

cat src/css/pages/page-*.css > src/css/pages.css
cat src/css/pages/service-*.css > src/css/services.css

Updating Content

HTML-based pages (Home, About, Services)

  1. Download updated HTML from WordPress
  2. Replace the corresponding file in src/data/
  3. Update the CSS in src/css/pages/ if Elementor page CSS changed
  4. Rebuild: npm run build

Componentized pages (Contact, Project, ServiceDetail)

  1. Edit the relevant section component in src/components/sections/
  2. For service detail content, edit src/data/services.json
  3. For contact data, edit Contact.jsx directly
  4. Rebuild: npm run build

Brand Colors

Edit .env (or create from .env.example), then rebuild:

VITE_WHATSAPP_PHONE=971564148980
VITE_BRAND_PRIMARY=#FA2D39
VITE_BRAND_DARK=#101840
VITE_BRAND_NAVY=#0a004c

Setup & Development

cd react-app
npm install
npm run dev          # Vite dev server (HMR enabled)

Build & Deploy

npm run build        # Outputs to dist/  — 0 errors, 0 warnings

The site is served via a Python3 HTTP server on port 8011:

cd dist && python3 -m http.server 8011

No CI/CD pipeline is configured — deploy is manual.

Production File Structure

dist/
├── index.html                          7.6 KB │ gzip: 2.1 KB
├── assets/index-*.css                 74.0 KB │ gzip: 5.4 KB
├── assets/index-*.js                 257.3 KB │ gzip: 80.9 KB  (main bundle: router, components, CSS)
├── assets/Home-*.js                   69.7 KB │ gzip: 10.8 KB
├── assets/SafeHtml-*.js               27.8 KB │ gzip: 10.1 KB  (html-react-parser chunk)
├── assets/Services-*.js               29.8 KB │ gzip: 4.5 KB
├── assets/About-*.js                  26.5 KB │ gzip: 3.9 KB
├── assets/ServiceDetail-*.js          25.1 KB │ gzip: 7.0 KB
├── assets/Contact-*.js                 9.2 KB │ gzip: 2.3 KB
├── assets/Project-*.js                 4.0 KB │ gzip: 1.5 KB
└── assets/Breadcrumb-*.js              0.6 KB │ gzip: 0.3 KB

All other assets (theme CSS/JS, Elementor assets, images) are served from public/wp-content/ and public/wp-includes/.

The build produces 0 errors, 0 warnings. If you see warnings, fix them before deploying.

Dependencies

Package Version Purpose
react ^19.2.6 UI framework
react-dom ^19.2.6 DOM renderer
react-router-dom ^7.17.0 SPA routing
html-react-parser ^6.1.4 Safe HTML-to-React parsing
@fortawesome/fontawesome-free ^7.3.0 Icon library
vite ^8.0.12 Build tool
@vitejs/plugin-react ^6.0.1 Vite React plugin
Description
No description provided
Readme 25 MiB
Languages
CSS 69.3%
JavaScript 30.4%
HTML 0.3%