Changeset 356478
- Timestamp:
- 11/01/2026 09:38:18 AM (less than one hour ago)
- Location:
- flowix/1.1.2
- Files:
-
- 13 added
- 3 deleted
- 36 edited
- 1 copied
-
. (copied) (copied from flowix/1.1.1 )
-
404.php (modified) (1 diff)
-
archive.php (modified) (1 diff)
-
assets/css/builder-controls-rtl.css (added)
-
assets/css/builder-controls.css (modified) (1 diff)
-
assets/css/element-styles-controls-rtl.css (added)
-
assets/css/flowix-history-rtl.css (added)
-
assets/css/flowix-history.css (added)
-
assets/css/header-builder-rtl.css (added)
-
assets/css/header-builder.css (modified) (4 diffs)
-
assets/css/home-sections-rtl.css (added)
-
assets/css/home-sections.css (modified) (1 diff)
-
assets/css/shop-features-rtl.css (added)
-
assets/css/shop-features.css (modified) (2 diffs)
-
assets/css/woocommerce-rtl.css (added)
-
assets/js/builder-controls.js (modified) (8 diffs)
-
assets/js/customizer-controls.js (modified) (1 diff)
-
assets/js/customizer-preview.js (modified) (2 diffs)
-
assets/js/element-styles-controls.js (modified) (9 diffs)
-
assets/js/element-styles-preview.js (modified) (2 diffs)
-
assets/js/flowix-history.js (added)
-
assets/js/main.js (modified) (9 diffs)
-
assets/js/shop-features.js (modified) (3 diffs)
-
footer.php (modified) (1 diff)
-
functions.php (modified) (13 diffs)
-
header.php (modified) (5 diffs)
-
inc/brand-kit.php (modified) (5 diffs)
-
inc/customizer-output.php (modified) (1 diff)
-
inc/customizer.php (modified) (5 diffs)
-
inc/design-gallery.php (modified) (3 diffs)
-
inc/editor-history.php (added)
-
inc/element-registry.php (modified) (1 diff)
-
inc/element-styles.php (modified) (6 diffs)
-
inc/footer-builder.php (modified) (9 diffs)
-
inc/header-builder.php (modified) (6 diffs)
-
inc/home-sections.php (modified) (14 diffs)
-
inc/shop-features.php (modified) (6 diffs)
-
inc/upsell.php (deleted)
-
index.php (modified) (1 diff)
-
languages (added)
-
languages/flowix.pot (added)
-
page.php (modified) (2 diffs)
-
readme.txt (modified) (3 diffs)
-
search.php (modified) (4 diffs)
-
single.php (modified) (2 diffs)
-
style-rtl.css (added)
-
style.css (modified) (9 diffs)
-
template-parts/home/featured.php (modified) (1 diff)
-
template-parts/home/page_content.php (modified) (1 diff)
-
template-parts/home/testimonials.php (deleted)
-
template-parts/home/trust.php (modified) (1 diff)
-
template-parts/home/vendor.php (deleted)
-
woocommerce.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
flowix/1.1.2/404.php
r319286 r356478 6 6 get_header(); ?> 7 7 <div class="container"> 8 <div class="error-404" style="padding:100px 20px;text-align:center;">8 <div class="error-404"> 9 9 <div class="error-404-number" aria-hidden="true">404</div> 10 10 <span class="eyebrow"><?php esc_html_e( 'Page Not Found', 'flowix' ); ?></span> 11 <h1 style="margin-bottom:16px;"><?php esc_html_e( "This page wandered off.", 'flowix' ); ?></h1>12 <p class="lead" style="max-width:440px;margin:0 auto 36px;">11 <h1><?php esc_html_e( "This page wandered off.", 'flowix' ); ?></h1> 12 <p class="lead"> 13 13 <?php esc_html_e( "The page you're looking for doesn't exist. Maybe it moved, or the link was a typo.", 'flowix' ); ?> 14 14 </p> 15 <div style="display:flex;gap:14px;justify-content:center;flex-wrap:wrap;">15 <div class="error-404-actions"> 16 16 <a href="<?php echo esc_url( home_url( '/' ) ); ?>" class="btn btn--primary btn--lg"> 17 17 <?php esc_html_e( 'Go Home', 'flowix' ); ?> -
flowix/1.1.2/archive.php
r353748 r356478 7 7 8 8 get_header(); ?> 9 <div class="container fx-blog" style="padding-top:48px;padding-bottom:64px;">10 <header class="fx-blog-header " style="margin-bottom:36px;padding-bottom:24px;border-bottom:1px solid var(--border-light);">9 <div class="container fx-blog"> 10 <header class="fx-blog-header fx-page-header"> 11 11 <?php the_archive_title( '<h1>', '</h1>' ); ?> 12 <?php the_archive_description( '<div class="lead " style="margin-top:8px;">', '</div>' ); ?>12 <?php the_archive_description( '<div class="lead fx-page-sub">', '</div>' ); ?> 13 13 </header> 14 14 <?php if ( have_posts() ) : ?> -
flowix/1.1.2/assets/css/builder-controls.css
r353588 r356478 86 86 .fx-gal-thumbs span { aspect-ratio: 1; background: #f0f0f1 center / cover; border-radius: 3px; } 87 87 .fx-gallery-field { flex-direction: column; align-items: stretch; } 88 .fx-cz-notice { display: block; padding: 8px 12px; margin: 0 0 12px; } 89 .fx-cz-notice .button { margin-top: 8px; } -
flowix/1.1.2/assets/css/header-builder.css
r353588 r356478 4 4 .fx-header .fx-hrow-inner { 5 5 display: grid !important; 6 grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr) !important; 6 /* Sides never squeeze below their content (logo, icons); a centred menu wraps instead. */ 7 grid-template-columns: minmax(min-content, 1fr) minmax(0, auto) minmax(min-content, 1fr) !important; 7 8 align-items: center; 8 9 gap: var(--space-lg, 24px); … … 28 29 /* Items */ 29 30 .fx-hitem--menu { display: flex; justify-content: center; } 31 /* A long menu wraps onto a second line instead of running under the logo or icons. */ 32 .fx-hzone .fx-hitem--menu { min-width: 0; } 33 .fx-hzone .fx-hitem--menu > ul { flex-wrap: wrap; row-gap: 2px; } 34 .fx-hzone--center .fx-hitem--menu > ul { justify-content: center; } 35 .fx-header .fx-desktop .fx-hitem--logo { flex-shrink: 0; white-space: nowrap; } 36 .fx-hitem--secondary-menu a { white-space: nowrap; } 30 37 .fx-hspacer { flex: 1 1 auto; } 31 38 .fx-hdivider { width: 1px; height: 22px; background: currentColor; opacity: 0.25; margin: 0 6px; } … … 99 106 ================================================================ */ 100 107 .fx-footer .fx-fmain { grid-template-columns: var(--fx-fgrid, 2fr 1fr 1fr 1fr); } 108 /* Column layout presets (Footer Builder → Column layout). */ 109 .fx-footer .fx-fmain.fx-fgrid--1fr { --fx-fgrid: 1fr; } 110 .fx-footer .fx-fmain.fx-fgrid--1fr-1fr { --fx-fgrid: 1fr 1fr; } 111 .fx-footer .fx-fmain.fx-fgrid--2fr-1fr { --fx-fgrid: 2fr 1fr; } 112 .fx-footer .fx-fmain.fx-fgrid--1fr-2fr { --fx-fgrid: 1fr 2fr; } 113 .fx-footer .fx-fmain.fx-fgrid--1fr-1fr-1fr { --fx-fgrid: 1fr 1fr 1fr; } 114 .fx-footer .fx-fmain.fx-fgrid--2fr-1fr-1fr { --fx-fgrid: 2fr 1fr 1fr; } 115 .fx-footer .fx-fmain.fx-fgrid--1fr-1fr-2fr { --fx-fgrid: 1fr 1fr 2fr; } 116 .fx-footer .fx-fmain.fx-fgrid--1fr-2fr-1fr { --fx-fgrid: 1fr 2fr 1fr; } 117 .fx-footer .fx-fmain.fx-fgrid--1fr-1fr-1fr-1fr { --fx-fgrid: 1fr 1fr 1fr 1fr; } 118 .fx-footer .fx-fmain.fx-fgrid--1p4fr-1fr-1fr-1fr { --fx-fgrid: 1.4fr 1fr 1fr 1fr; } 119 .fx-footer .fx-fmain.fx-fgrid--2fr-1fr-1fr-1fr { --fx-fgrid: 2fr 1fr 1fr 1fr; } 120 .fx-footer .fx-fmain.fx-fgrid--1fr-1fr-1fr-2fr { --fx-fgrid: 1fr 1fr 1fr 2fr; } 121 .fx-footer .fx-fmain.fx-fgrid--1fr-1fr-1fr-1fr-1fr { --fx-fgrid: 1fr 1fr 1fr 1fr 1fr; } 122 .fx-footer .fx-fmain.fx-fgrid--2fr-1fr-1fr-1fr-1fr { --fx-fgrid: 2fr 1fr 1fr 1fr 1fr; } 123 .fx-footer .fx-fmain.fx-fgrid--1fr-1fr-1fr-1fr-1fr-1fr { --fx-fgrid: 1fr 1fr 1fr 1fr 1fr 1fr; } 101 124 .fx-footer .fx-fmain > div:last-child { display: block; } /* old theme rule hid the last column on tablets */ 102 125 .fx-fcol { min-width: 0; display: flex; flex-direction: column; gap: 22px; } … … 109 132 .fx-fcontact svg { flex: none; margin-top: 3px; opacity: 0.7; } 110 133 .fx-fcontact a { color: inherit; } 111 .fx-fnewsletter { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 10px; }112 .fx-fnewsletter input[type="email"] { flex: 1 1 160px; min-width: 0; padding: 10px 12px; border-radius: var(--radius-sm, 4px); border: 1px solid color-mix(in srgb, var(--ft-tx, #faf7f2) 25%, transparent); background: color-mix(in srgb, var(--ft-tx, #faf7f2) 6%, transparent); color: inherit; }113 134 .fx-fbacktotop { display: inline-flex; align-items: center; gap: 6px; font-size: 0.75rem; color: inherit; text-decoration: none; opacity: 0.8; } 114 135 .fx-fbacktotop:hover { opacity: 1; } -
flowix/1.1.2/assets/css/home-sections.css
r353588 r356478 6 6 .fx-sec-empty { padding: 24px; border: 2px dashed var(--border-light, #e0d6c6); border-radius: 8px; text-align: center; color: var(--walnut, #6b4e33); } 7 7 8 /* Image banner */9 .fx-sec--banner {10 display: flex;11 align-items: center;12 background: var(--espresso, #2d1a0e) var(--fx-banner-img, none) center / cover no-repeat;13 color: var(--cream, #faf7f2);14 isolation: isolate;15 }16 .fx-sec--banner::before { content: ""; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0.2)); z-index: -1; }17 .fx-banner--s { min-height: 280px; }18 .fx-banner--m { min-height: 440px; }19 .fx-banner--l { min-height: 620px; }20 .fx-banner--full { min-height: 100vh; }21 .fx-banner-inner { width: 100%; display: flex; }22 .fx-banner--center .fx-banner-inner { justify-content: center; text-align: center; }23 .fx-banner--right .fx-banner-inner { justify-content: flex-end; }24 .fx-banner-content { max-width: 620px; padding: 48px 0; }25 .fx-banner-title { color: inherit; font-size: clamp(2rem, 5vw, 3.6rem); margin: 8px 0 14px; }26 .fx-banner-text { font-size: 1.1rem; opacity: 0.9; margin-bottom: 24px; }27 .fx-banner-eyebrow { color: var(--gold-light, #d4b57a); }28 8 29 /* Image + text */ 30 .fx-ti-inner { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(28px, 5vw, 72px); align-items: center; } 31 .fx-ti--right .fx-ti-media { order: 2; } 32 .fx-ti-img { width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--radius-md, 8px); display: block; } 33 .fx-ti-placeholder { background: var(--linen, #f0e8dc); } 34 .fx-ti-content .section-heading { margin-bottom: 18px; text-align: left; } 35 .fx-ti-text { margin-bottom: 24px; line-height: 1.75; } 36 37 /* Feature icons */ 38 .fx-features { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 28px; } 39 .fx-feature { text-align: center; padding: 28px 20px; border-radius: var(--radius-md, 8px); background: #fff; border: 1px solid var(--border-light, #e8e0d3); } 40 .fx-feature-icon { display: inline-flex; width: 60px; height: 60px; align-items: center; justify-content: center; border-radius: 50%; background: var(--linen, #f0e8dc); color: var(--gold, #9b7b3a); margin-bottom: 14px; } 41 .fx-feature-title { font-size: 1.15rem; margin-bottom: 6px; } 42 .fx-feature-text { font-size: 0.92rem; opacity: 0.8; margin: 0; } 43 44 /* Call to action & countdown */ 45 .fx-sec--cta, .fx-sec--countdown { background: var(--linen, #f0e8dc); } 46 .fx-cta-inner { text-align: center; max-width: 760px; } 47 .fx-cta-text { font-size: 1.05rem; margin-bottom: 8px; } 48 .fx-countdown { display: flex; justify-content: center; gap: clamp(8px, 2vw, 18px); margin-top: 22px; flex-wrap: wrap; } 49 .fx-cd-unit { min-width: 84px; padding: 16px 10px; background: var(--espresso, #2d1a0e); color: var(--cream, #faf7f2); border-radius: var(--radius-md, 8px); display: flex; flex-direction: column; align-items: center; } 50 .fx-cd-num { font-family: var(--font-display); font-size: clamp(1.8rem, 4vw, 2.6rem); line-height: 1; font-variant-numeric: tabular-nums; } 51 .fx-cd-label { font-family: var(--font-ui); font-size: 0.62rem; letter-spacing: 0.16em; text-transform: uppercase; margin-top: 6px; opacity: 0.75; } 52 .fx-countdown.is-over { font-size: 1.2rem; font-weight: 600; } 53 54 /* Ticker */ 55 .fx-sec--ticker { overflow: hidden; background: var(--espresso, #2d1a0e); color: var(--cream, #faf7f2); padding: 14px 0; } 56 .fx-ticker-track { display: flex; width: max-content; animation: fx-ticker var(--fx-ticker-dur, 30s) linear infinite; } 57 .fx-ticker--right .fx-ticker-track { animation-direction: reverse; } 58 .fx-sec--ticker:hover .fx-ticker-track { animation-play-state: paused; } 59 .fx-ticker-run { display: flex; align-items: center; flex: none; } 60 .fx-ticker-item { font-family: var(--font-ui); font-size: 0.8rem; letter-spacing: 0.14em; text-transform: uppercase; white-space: nowrap; padding: 0 22px; } 61 .fx-ticker-sep { color: var(--gold-light, #d4b57a); } 62 @keyframes fx-ticker { from { transform: translateX(0); } to { transform: translateX(-50%); } } 63 @media (prefers-reduced-motion: reduce) { .fx-ticker-track { animation: none; } } 64 65 /* Video */ 66 .fx-video { position: relative; border-radius: var(--radius-md, 8px); overflow: hidden; background: #000; } 67 .fx-video video, .fx-video iframe { display: block; width: 100%; aspect-ratio: 16 / 9; height: auto; border: 0; } 68 .fx-video-caption { text-align: center; margin-top: 12px; font-size: 0.9rem; opacity: 0.8; } 69 70 @media (max-width: 782px) { 71 .fx-ti-inner { grid-template-columns: 1fr; } 72 .fx-ti--right .fx-ti-media { order: 0; } 73 } 74 .fx-video .fx-sec-empty { color: #fff; border-color: rgba(255, 255, 255, 0.4); margin: 0; } 9 /* Spacer section heights (Homepage Sections → Spacer), in steps of 10px. */ 10 .fx-spacer--0 { height: 0px; } 11 .fx-spacer--10 { height: 10px; } 12 .fx-spacer--20 { height: 20px; } 13 .fx-spacer--30 { height: 30px; } 14 .fx-spacer--40 { height: 40px; } 15 .fx-spacer--50 { height: 50px; } 16 .fx-spacer--60 { height: 60px; } 17 .fx-spacer--70 { height: 70px; } 18 .fx-spacer--80 { height: 80px; } 19 .fx-spacer--90 { height: 90px; } 20 .fx-spacer--100 { height: 100px; } 21 .fx-spacer--110 { height: 110px; } 22 .fx-spacer--120 { height: 120px; } 23 .fx-spacer--130 { height: 130px; } 24 .fx-spacer--140 { height: 140px; } 25 .fx-spacer--150 { height: 150px; } 26 .fx-spacer--160 { height: 160px; } 27 .fx-spacer--170 { height: 170px; } 28 .fx-spacer--180 { height: 180px; } 29 .fx-spacer--190 { height: 190px; } 30 .fx-spacer--200 { height: 200px; } 31 .fx-spacer--210 { height: 210px; } 32 .fx-spacer--220 { height: 220px; } 33 .fx-spacer--230 { height: 230px; } 34 .fx-spacer--240 { height: 240px; } 35 .fx-spacer--250 { height: 250px; } 36 .fx-spacer--260 { height: 260px; } 37 .fx-spacer--270 { height: 270px; } 38 .fx-spacer--280 { height: 280px; } 39 .fx-spacer--290 { height: 290px; } 40 .fx-spacer--300 { height: 300px; } 41 .fx-spacer--310 { height: 310px; } 42 .fx-spacer--320 { height: 320px; } 43 .fx-spacer--330 { height: 330px; } 44 .fx-spacer--340 { height: 340px; } 45 .fx-spacer--350 { height: 350px; } 46 .fx-spacer--360 { height: 360px; } 47 .fx-spacer--370 { height: 370px; } 48 .fx-spacer--380 { height: 380px; } 49 .fx-spacer--390 { height: 390px; } 50 .fx-spacer--400 { height: 400px; } 51 .fx-spacer--410 { height: 410px; } 52 .fx-spacer--420 { height: 420px; } 53 .fx-spacer--430 { height: 430px; } 54 .fx-spacer--440 { height: 440px; } 55 .fx-spacer--450 { height: 450px; } 56 .fx-spacer--460 { height: 460px; } 57 .fx-spacer--470 { height: 470px; } 58 .fx-spacer--480 { height: 480px; } 59 .fx-spacer--490 { height: 490px; } 60 .fx-spacer--500 { height: 500px; } 61 .fx-spacer--510 { height: 510px; } 62 .fx-spacer--520 { height: 520px; } 63 .fx-spacer--530 { height: 530px; } 64 .fx-spacer--540 { height: 540px; } 65 .fx-spacer--550 { height: 550px; } 66 .fx-spacer--560 { height: 560px; } 67 .fx-spacer--570 { height: 570px; } 68 .fx-spacer--580 { height: 580px; } 69 .fx-spacer--590 { height: 590px; } 70 .fx-spacer--600 { height: 600px; } -
flowix/1.1.2/assets/css/shop-features.css
r353588 r356478 11 11 .woocommerce div.product > .fx-badges { top: 16px; left: 16px; } 12 12 13 /* ── Card buttons (wishlist, quick view) ── */14 .fx-card-btns { position: absolute; top: 12px; right: 12px; display: flex; flex-direction: column; gap: 8px; z-index: 4; }15 .fx-card-btn {16 width: 38px; height: 38px; border-radius: 50%; border: 0; padding: 0; cursor: pointer;17 display: grid; place-items: center;18 background: var(--bg-surface, #fff); color: var(--tx-strong, #1c0f06);19 box-shadow: 0 4px 14px var(--shadow-warm, rgba(0,0,0,.12));20 opacity: 0; transform: translateX(8px);21 transition: opacity .25s, transform .25s, background .2s, color .2s;22 }23 .fx-card-btn:nth-child(2) { transition-delay: .05s; }24 .product-card:hover .fx-card-btn, li.product:hover .fx-card-btn, .fx-card-btn:focus-visible, .fx-wl-btn.is-on { opacity: 1; transform: none; }25 @media (hover: none) { .fx-card-btn { opacity: 1; transform: none; } }26 .fx-card-btn:hover { background: var(--tx-strong, #1c0f06); color: var(--bg-page, #fff); }27 .fx-card-btn svg { pointer-events: none; }28 .fx-wl-btn.is-on { color: var(--terracotta, #c0392b); }29 .fx-wl-btn.is-on svg { fill: currentColor; }30 .fx-wl-btn.is-on:hover { background: var(--bg-surface, #fff); color: var(--terracotta, #c0392b); }31 .fx-pop svg { animation: fx-pop .45s cubic-bezier(.3,1.6,.5,1); }32 @keyframes fx-pop { 0% { transform: scale(1); } 40% { transform: scale(1.45); } 100% { transform: scale(1); } }33 .fx-card-btn.is-loading { animation: fx-pulse 0.8s infinite alternate; }34 @keyframes fx-pulse { to { opacity: .45; } }35 36 /* Colour dots on cards */37 .fx-card-dots { display: flex; gap: 5px; margin-top: 4px; flex-wrap: wrap; }38 li.product .fx-card-dots { justify-content: inherit; padding: 0 0 6px; }39 .fx-dot { width: 12px; height: 12px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,.15); }40 41 /* ── Quick view ── */42 html.fx-qv-lock { overflow: hidden; }43 .fx-qv-modal { position: fixed; inset: 0; z-index: 100000; display: grid; place-items: center; padding: 16px; }44 .fx-qv-backdrop { position: absolute; inset: 0; background: rgba(15,10,5,.55); backdrop-filter: blur(3px); opacity: 0; transition: opacity .25s; }45 .fx-qv-dialog {46 position: relative; width: min(960px, 100%); max-height: calc(100vh - 32px); overflow: auto;47 background: var(--bg-page, #fff); color: var(--tx-strong, #1c0f06); border-radius: var(--radius-lg, 8px);48 box-shadow: 0 30px 80px rgba(0,0,0,.35);49 opacity: 0; transform: translateY(20px) scale(.97); transition: opacity .25s, transform .25s;50 }51 .fx-qv-modal.is-open .fx-qv-backdrop, .fx-qv-modal.is-open .fx-qv-dialog { opacity: 1; transform: none; }52 .fx-qv-close { position: absolute; top: 10px; right: 12px; z-index: 5; width: 40px; height: 40px; border: 0; border-radius: 50%; background: var(--bg-surface, #fff); color: inherit; font-size: 26px; line-height: 1; cursor: pointer; box-shadow: 0 2px 10px rgba(0,0,0,.12); }53 .fx-qv { display: grid; grid-template-columns: 1fr 1fr; }54 .fx-qv-media { position: relative; background: var(--bg-tint2, var(--bg-tint, #f6f1ea)); }55 .fx-qv-main img { display: block; width: 100%; height: auto; aspect-ratio: 1; object-fit: cover; }56 .fx-qv-thumbs { display: flex; gap: 6px; padding: 8px; overflow-x: auto; }57 .fx-qv-thumb { flex: 0 0 56px; padding: 0; border: 2px solid transparent; border-radius: 4px; overflow: hidden; cursor: pointer; background: none; }58 .fx-qv-thumb.is-on, .fx-qv-thumb:hover { border-color: var(--tx-strong, #1c0f06); }59 .fx-qv-thumb img { display: block; width: 100%; height: 56px; object-fit: cover; }60 .fx-qv-summary { padding: 36px 32px 28px; display: flex; flex-direction: column; gap: 10px; }61 .fx-qv-title { margin: 0 30px 0 0; }62 .fx-qv-summary .price { font-size: 1.3rem; }63 .fx-qv-summary form.cart { margin: 8px 0 0; }64 .fx-qv-more { margin-top: auto; font-weight: 600; font-size: .9rem; }65 .fx-qv-dialog .fx-qv.product div.summary { width: auto !important; float: none !important; margin: 0 !important; }66 .fx-qv .fx-qv-summary .woocommerce-product-details__short-description, .fx-qv-summary p { max-width: none; }67 .fx-qv table.variations, .fx-qv table.variations tbody, .fx-qv table.variations tr, .fx-qv table.variations th, .fx-qv table.variations td { display: block; width: auto; padding: 0; text-align: left; }68 .fx-qv table.variations tr { margin-bottom: 14px; }69 .fx-qv table.variations th { margin-bottom: 8px; }70 .fx-qv form.cart .woocommerce-variation-add-to-cart, .fx-qv form.cart:not(.variations_form) { display: flex; flex-wrap: wrap; gap: 8px; align-items: stretch; }71 .fx-qv form.cart .fx-buy-now { margin-left: 0; }72 @media (max-width: 700px) { .fx-qv { grid-template-columns: 1fr; } .fx-qv-summary { padding: 22px 18px; } }73 74 /* ── Swatches ── */75 .fx-swatch-wrap select { position: absolute !important; width: 1px !important; height: 1px !important; opacity: 0 !important; pointer-events: none; }76 .fx-swatch-wrap { position: relative; }77 .fx-swatches { display: flex; flex-wrap: wrap; gap: 8px; }78 .fx-swatch {79 --fx-sw: transparent;80 min-width: 36px; height: 36px; padding: 0; border: 1px solid var(--bd-light, #ddd); border-radius: 50%;81 background: var(--fx-sw); color: var(--tx-strong, #1c0f06); cursor: pointer; overflow: hidden;82 display: inline-grid; place-items: center; font: 600 .8rem/1 var(--font-ui, inherit);83 box-shadow: 0 0 0 2px var(--bg-page, #fff) inset; transition: box-shadow .15s, transform .15s, border-color .15s;84 }85 .fx-swatches--rounded .fx-swatch { border-radius: 8px; }86 .fx-swatches--square .fx-swatch { border-radius: 0; }87 .fx-swatch.is-label { padding: 0 14px; border-radius: 999px; background: var(--bg-surface, #fff); box-shadow: none; }88 .fx-swatches--rounded .fx-swatch.is-label { border-radius: 8px; }89 .fx-swatches--square .fx-swatch.is-label { border-radius: 0; }90 .fx-swatch.is-image img { width: 100%; height: 100%; object-fit: cover; }91 .fx-swatch.is-image { width: 44px; height: 44px; }92 .fx-swatch:hover { transform: translateY(-1px); border-color: var(--tx-strong, #1c0f06); }93 .fx-swatch.is-selected { border-color: var(--tx-strong, #1c0f06); box-shadow: 0 0 0 2px var(--bg-page, #fff) inset, 0 0 0 2px var(--tx-strong, #1c0f06); }94 .fx-swatch.is-label.is-selected { background: var(--tx-strong, #1c0f06); color: var(--bg-page, #fff); box-shadow: none; }95 .fx-swatch.is-disabled { opacity: .3; cursor: not-allowed; position: relative; }96 .fx-swatch.is-disabled::after { content: ""; position: absolute; left: 50%; top: -4px; bottom: -4px; width: 1px; background: currentColor; transform: rotate(45deg); }97 .fx-swatch-chosen { display: block; margin-top: 6px; font-size: .85rem; color: var(--tx-muted, #6b5a4c); min-height: 1.2em; }98 .woocommerce div.product form.cart .variations td, .woocommerce div.product form.cart .variations th { vertical-align: top; padding-bottom: 14px; }99 100 /* ── Sale countdown & urgency ── */101 .fx-sale-cd { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px; margin: 4px 0 16px; padding: 12px 14px; border-radius: var(--radius-md, 4px); background: color-mix(in srgb, var(--terracotta, #c0392b) 9%, transparent); color: var(--terracotta, #c0392b); font-weight: 600; }102 .fx-sale-cd-label { display: inline-flex; align-items: center; gap: 6px; }103 .fx-sale-cd .fx-countdown--inline { display: flex; gap: 6px; }104 .fx-sale-cd .fx-cd-unit { display: inline-flex; align-items: baseline; gap: 1px; padding: 4px 7px; border-radius: 4px; background: var(--bg-page, #fff); color: var(--tx-strong, #1c0f06); font-variant-numeric: tabular-nums; }105 .fx-sale-cd .fx-cd-num { font-size: 1rem; font-weight: 700; }106 .fx-sale-cd .fx-cd-label { font-size: .7rem; opacity: .7; }107 .fx-urgency { margin: 0 0 16px; }108 .fx-urgency p { display: flex; align-items: center; gap: 6px; margin: 0 0 6px; font-weight: 600; color: var(--terracotta, #c0392b); }109 .fx-urgency-bar { display: block; height: 6px; border-radius: 6px; background: var(--bd-light, #eee); overflow: hidden; }110 .fx-urgency-bar span { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--terracotta, #c0392b), var(--amber, #d89a3c)); }111 112 /* ── Buy now ── */113 form.cart .fx-buy-now, .woocommerce div.product form.cart .fx-buy-now {114 margin-left: 8px; padding: 15px 30px; flex: 1;115 background: var(--fx-accent, var(--gold, #b8893a)); border: 1px solid var(--fx-accent, var(--gold, #b8893a)); color: #fff !important;116 font: 700 .75rem/1 var(--font-ui, inherit); letter-spacing: .2em; text-transform: uppercase; cursor: pointer;117 }118 form.cart .fx-buy-now:hover { filter: brightness(1.08); }119 .woocommerce-variation-add-to-cart-disabled .fx-buy-now { opacity: .5; pointer-events: none; }120 li.product .fx-card-dots { padding: 0 18px 6px; }121 form.cart.variations_form .fx-buy-now, form.cart .fx-buy-now { vertical-align: middle; }122 @media (max-width: 480px) { form.cart .fx-buy-now { width: 100%; margin: 10px 0 0 !important; } }123 13 124 14 /* ── Delivery & trust ── */ … … 175 65 @media (max-width: 600px) { .fx-toast { right: 16px; bottom: 16px; } } 176 66 177 /* ── Load more ── */178 .fx-loadmore { display: flex; justify-content: center; margin: 32px 0; }179 .fx-new-item { opacity: 0; transform: translateY(12px); }180 ul.products > *, .products-grid > * { transition: opacity .4s, transform .4s; }181 67 182 /* ── Product lists (wishlist, recently viewed) ──*/183 .fx- plist.is-loading .fx-plist-grid { min-height: 200px; opacity: .5; }184 .fx- plist-empty { text-align: center; padding: 40px 16px; color: var(--tx-muted, #6b5a4c); }185 .fx-f orm-error { margin-bottom: 12px; }186 187 /* Options table above the price/add-to-cart row (WooCommerce puts them side by side, which squeezes swatches). */ 188 . woocommerce div.product form.variations_form, .fx-qv form.variations_form { display: block; }189 . woocommerce div.product form.variations_form table.variations { width: 100%; margin-bottom: 8px; }190 . woocommerce div.product form.variations_form .woocommerce-variation-add-to-cart { display: flex; flex-wrap: wrap; gap: 8px; align-items: stretch; }191 . woocommerce div.product form.variations_form .woocommerce-variation-add-to-cart .fx-buy-now { margin-left: 0; }192 . woocommerce div.product form.variations_form .woocommerce-variation-description p { margin: 0 0 12px; }193 .fx- qv-media { align-self: start; }194 .fx- qv .variations th, .fx-qv .variations td, .fx-qv .variations label { padding: 0 !important; margin: 0 !important; line-height: 1.4; }195 .fx- qv table.variations th { margin-bottom: 8px !important; }196 .fx- qv table.variations { margin: 6px 0 14px !important; }197 198 /* Phones: quantity + Add to cart on one row, Buy now full width underneath. */ 199 @media (max-width: 600px) { 200 .woocommerce div.product form.cart:not(.variations_form), .woocommerce div.product form.cart .woocommerce-variation-add-to-cart { display: flex; flex-wrap: wrap; gap: 8px; }201 .woocommerce div.product form.cart .single_add_to_cart_button { flex: 1 1 auto; padding-left: 16px !important; padding-right: 16px !important; white-space: nowrap; }202 .woocommerce div.product form.cart .fx-buy-now { flex: 1 1 100%; margin: 0 !important; }203 }68 /* Free-delivery bar fill, in steps of 5%. */ 69 .fx-fsb-fill--0 { width: 0%; } 70 .fx-fsb-fill--5 { width: 5%; } 71 .fx-fsb-fill--10 { width: 10%; } 72 .fx-fsb-fill--15 { width: 15%; } 73 .fx-fsb-fill--20 { width: 20%; } 74 .fx-fsb-fill--25 { width: 25%; } 75 .fx-fsb-fill--30 { width: 30%; } 76 .fx-fsb-fill--35 { width: 35%; } 77 .fx-fsb-fill--40 { width: 40%; } 78 .fx-fsb-fill--45 { width: 45%; } 79 .fx-fsb-fill--50 { width: 50%; } 80 .fx-fsb-fill--55 { width: 55%; } 81 .fx-fsb-fill--60 { width: 60%; } 82 .fx-fsb-fill--65 { width: 65%; } 83 .fx-fsb-fill--70 { width: 70%; } 84 .fx-fsb-fill--75 { width: 75%; } 85 .fx-fsb-fill--80 { width: 80%; } 86 .fx-fsb-fill--85 { width: 85%; } 87 .fx-fsb-fill--90 { width: 90%; } 88 .fx-fsb-fill--95 { width: 95%; } 89 .fx-fsb-fill--100 { width: 100%; } -
flowix/1.1.2/assets/js/builder-controls.js
r353748 r356478 24 24 return fallback; 25 25 } 26 } 27 28 /** Redraw a builder when its setting is changed from elsewhere (undo, a look, import). */ 29 function follow( builder, load ) { 30 builder.written = builder.control.setting.get(); 31 builder.control.setting.bind( function( v ) { 32 if ( v === builder.written ) { 33 return; 34 } 35 builder.written = v; 36 load( v ); 37 builder.open = null; 38 builder.render(); 39 } ); 26 40 } 27 41 … … 39 53 this.render(); 40 54 var self = this; 55 follow( this, function( v ) { 56 self.cfg = $.extend( true, {}, B.header.defaults, parse( v, {} ) ); 57 } ); 41 58 if ( api.previewedDevice ) { 42 59 api.previewedDevice.bind( function( d ) { … … 51 68 52 69 HeaderBuilder.prototype.save = function() { 53 this.control.setting.set( JSON.stringify( this.cfg ) ); 70 this.written = JSON.stringify( this.cfg ); 71 this.control.setting.set( this.written ); 54 72 }; 55 73 … … 262 280 this.open = null; 263 281 this.render(); 282 var self = this; 283 follow( this, function( v ) { 284 self.cfg = $.extend( true, {}, B.footer.defaults, parse( v, {} ) ); 285 } ); 264 286 } 265 287 … … 268 290 269 291 FooterBuilder.prototype.save = function() { 270 this.control.setting.set( JSON.stringify( this.cfg ) ); 292 this.written = JSON.stringify( this.cfg ); 293 this.control.setting.set( this.written ); 271 294 }; 272 295 … … 438 461 categories: 'flowix_categories_section', 439 462 featured: 'flowix_featured', 440 testimonials: 'flowix_testimonials',441 vendor: 'flowix_vendor_cta',442 463 page_content: 'static_front_page', 443 464 }; … … 461 482 self.renderNotice(); 462 483 } ); 484 follow( this, function( v ) { 485 var l = parse( v, null ); 486 self.list = Array.isArray( l ) ? l : $.extend( true, [], B.sections.initial ); 487 self.list.forEach( function( sec ) { 488 if ( ! sec.d || Array.isArray( sec.d ) ) { 489 sec.d = {}; 490 } 491 } ); 492 } ); 463 493 } 464 494 … … 467 497 clearTimeout( this.timer ); 468 498 var run = function() { 469 self.control.setting.set( JSON.stringify( self.list ) ); 499 self.written = JSON.stringify( self.list ); 500 self.control.setting.set( self.written ); 470 501 }; 471 502 if ( later ) { -
flowix/1.1.2/assets/js/customizer-controls.js
r319286 r356478 19 19 'flowix_cats', 20 20 'flowix_feat', 21 'flowix_testi',22 'flowix_vendor',23 'flowix_news',24 21 'flowix_footer_sec', 25 22 'flowix_header', -
flowix/1.1.2/assets/js/customizer-preview.js
r319286 r356478 191 191 192 192 /* ── FOOTER ───────────────────────────────────────────────────── */ 193 bind( 'flowix_footer_tagline', function( val ) {194 document.querySelectorAll( '.footer-brand-desc' ).forEach( function(e){e.textContent=val;} );195 } );196 193 bind( 'flowix_footer_text_color', function( val ) { 197 194 var footer = document.querySelector( '#colophon' ); … … 205 202 bindSectionBg( 'flowix_cats', '#categories-section' ); 206 203 bindSectionBg( 'flowix_feat', '#featured-section' ); 207 bindSectionBg( 'flowix_testi', '#testimonials-section' );208 bindSectionBg( 'flowix_vendor', '.vendor-cta' );209 bindSectionBg( 'flowix_news', '.newsletter-section' );210 204 bindSectionBg( 'flowix_footer_sec', '#colophon' ); 211 205 -
flowix/1.1.2/assets/js/element-styles-controls.js
r353588 r356478 527 527 $wrap.addClass( 'is-set' ); 528 528 } ); 529 var $transparent = el( 'button', { type: 'button', class: 'button button-small flowix-transparent', text: 'transparent'} ).on( 'click', function() {529 var $transparent = el( 'button', { type: 'button', class: 'button button-small flowix-transparent', text: T.transparent } ).on( 'click', function() { 530 530 tok = null; 531 531 self.write( key, 'transparent', true ); … … 811 811 var $mock = el( 'div', { class: 'flowix-look-mock' } ).css( { background: c.background, color: c.text, fontFamily: "'" + f.body + "', sans-serif" } ); 812 812 $mock.append( el( 'div', { class: 'flowix-look-bar' } ).css( { background: c.header || c.primary, borderBottom: '1px solid rgba(0,0,0,.08)' } ).append( 813 el( 'span', { text: 'Shop'} ).css( { color: c.header_text || '#fff', fontFamily: "'" + f.display + "', serif" } ),813 el( 'span', { text: T.lookShop } ).css( { color: c.header_text || '#fff', fontFamily: "'" + f.display + "', serif" } ), 814 814 el( 'i' ).css( 'background', c.accent ) 815 815 ) ); 816 816 $mock.append( el( 'div', { class: 'flowix-look-hero' } ).css( 'background', c.hero || c.primary ).append( 817 el( 'b', { text: 'Live better.'} ).css( { fontFamily: "'" + f.display + "', serif", color: c.hero_text || '#fff' } ),818 el( 'span', { text: 'Shop now'} ).css( { background: c.accent, color: c.background, borderRadius: Math.min( 30, r * 2 ) + 'px' } )817 el( 'b', { text: T.lookTagline } ).css( { fontFamily: "'" + f.display + "', serif", color: c.hero_text || '#fff' } ), 818 el( 'span', { text: T.lookButton } ).css( { background: c.accent, color: c.background, borderRadius: Math.min( 30, r * 2 ) + 'px' } ) 819 819 ) ); 820 820 var $cards = el( 'div', { class: 'flowix-look-cards' } ); … … 829 829 var $apply = el( 'button', { type: 'button', class: 'button button-primary', text: T.applyLook } ).on( 'click', function() { 830 830 if ( window.confirm( T.confirmLook.replace( '%s', look.name ) ) ) { 831 historyName( 'look', look.name ); 831 832 applyLook( look, layoutToo ); 832 833 $root.find( '.flowix-look' ).removeClass( 'is-applied' ); … … 868 869 869 870 871 /** Names the next undo step (see flowix-history.js), e.g. "Look: Surf Club". */ 872 function historyName( kind, detail ) { 873 if ( window.flowixHistory ) { 874 window.flowixHistory.name( kind, detail ); 875 } 876 } 877 870 878 function applyLook( look, layout ) { 871 879 api( 'flowix_brand' ).set( JSON.stringify( look.brand ) ); … … 904 912 var cur = parse( api( 'flowix_home_sections' ).get() ); 905 913 if ( ! Array.isArray( cur ) || ! cur.length ) { 906 cur = [ 'hero', 'trust', 'categories', 'featured', 'testimonials', 'vendor' ].map( function( t ) { 907 return { id: t, type: t, on: true, d: {} }; 908 } ); 914 cur = window.flowixBuilder && window.flowixBuilder.sections ? ( window.flowixBuilder.sections.initial || [] ) : []; 909 915 } 910 916 var defs = window.flowixBuilder && window.flowixBuilder.sections ? window.flowixBuilder.sections.defaults || {} : {}; … … 946 952 var b = $.extend( true, {}, D.brand.defaults, parse( control.setting.get() ) ); 947 953 var timer; 954 var written = control.setting.get(); 948 955 var save = function( now ) { 949 956 clearTimeout( timer ); 950 957 var run = function() { 951 control.setting.set( JSON.stringify( b ) ); 958 written = JSON.stringify( b ); 959 control.setting.set( written ); 952 960 }; 953 961 if ( now ) { … … 1093 1101 }; 1094 1102 render(); 1103 // Changed from elsewhere (undo, a look, import): redraw with the new values. 1104 control.setting.bind( function( v ) { 1105 if ( v !== written ) { 1106 clearTimeout( timer ); 1107 written = v; 1108 b = $.extend( true, {}, D.brand.defaults, parse( v ) ); 1109 render(); 1110 } 1111 } ); 1095 1112 } 1096 1113 … … 1153 1170 } 1154 1171 var n = 0; 1172 historyName( 'import', file.name ); 1155 1173 Object.keys( data.styles ).forEach( function( id ) { 1156 1174 if ( api( id ) && ( id.indexOf( 'flowix_el_' ) === 0 || [ 'flowix_custom_elements', 'flowix_site_fx', 'flowix_brand', 'flowix_styles', 'flowix_header', 'flowix_footer', 'flowix_home_sections' ].indexOf( id ) !== -1 ) ) { … … 1169 1187 return; 1170 1188 } 1189 historyName( 'resetAll' ); 1171 1190 flowixSettingIds().forEach( function( id ) { 1172 1191 api( id ).set( id === 'flowix_custom_elements' ? '[]' : '' ); -
flowix/1.1.2/assets/js/element-styles-preview.js
r353588 r356478 55 55 56 56 function setStyleTag( key, css ) { 57 var $tag = $( '#flowix-el-' + key ); 57 // Element stylesheets are enqueued as "flowix-el-{key}" (WordPress prints them as #flowix-el-{key}-inline-css). 58 var $tag = $( '#flowix-el-' + key + '-inline-css, #flowix-el-' + key ).first(); 58 59 if ( ! $tag.length ) { 59 $tag = $( '<style id="flowix-el-' + key + ' "></style>' ).appendTo( 'head' );60 $tag = $( '<style id="flowix-el-' + key + '-inline-css"></style>' ).appendTo( 'head' ); 60 61 } 61 62 $tag.text( css ); … … 83 84 setStyleTag( key, r.css ); 84 85 if ( key === 'brand' ) { 85 $( '#flowix-brand ' ).text( r.css );86 $( '#flowix-brand-inline-css' ).text( r.css ); 86 87 if ( r.dark ) { 87 88 document.documentElement.setAttribute( 'data-flowix-dark', r.dark.mode ); -
flowix/1.1.2/assets/js/main.js
r353588 r356478 25 25 function on( el, ev, fn ) { if ( el ) el.addEventListener( ev, fn ); } 26 26 27 /* ── KEYBOARD: keep focus inside an open panel, give it back on close ── */ 28 var FOCUSABLE = 'a[href], area[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])'; 29 var trapPanel = null; 30 var trapReturn = null; 31 function focusables( panel ) { 32 return Array.prototype.filter.call( panel.querySelectorAll( FOCUSABLE ), function ( el ) { 33 return el.offsetWidth || el.offsetHeight || el.getClientRects().length; 34 } ); 35 } 36 function trapOpen( panel, first ) { 37 trapPanel = panel; 38 trapReturn = document.activeElement; 39 panel.removeAttribute( 'aria-hidden' ); 40 setTimeout( function () { 41 var list = focusables( panel ); 42 var target = first || list[ 0 ] || panel; 43 if ( target === panel ) panel.setAttribute( 'tabindex', '-1' ); 44 target.focus(); 45 }, 60 ); 46 } 47 function trapClose( panel ) { 48 panel.setAttribute( 'aria-hidden', 'true' ); 49 if ( trapPanel !== panel ) return; 50 trapPanel = null; 51 if ( trapReturn && trapReturn.focus && document.contains( trapReturn ) ) trapReturn.focus(); 52 trapReturn = null; 53 } 54 document.addEventListener( 'keydown', function ( e ) { 55 if ( e.key !== 'Tab' || ! trapPanel ) return; 56 var list = focusables( trapPanel ); 57 if ( ! list.length ) { e.preventDefault(); return; } 58 var first = list[ 0 ]; 59 var last = list[ list.length - 1 ]; 60 if ( e.shiftKey && ( document.activeElement === first || ! trapPanel.contains( document.activeElement ) ) ) { 61 e.preventDefault(); 62 last.focus(); 63 } else if ( ! e.shiftKey && ( document.activeElement === last || ! trapPanel.contains( document.activeElement ) ) ) { 64 e.preventDefault(); 65 first.focus(); 66 } 67 } ); 68 [ searchOverlay, miniCartDrawer, mobileDrawer ].forEach( function ( panel ) { 69 if ( panel ) panel.setAttribute( 'aria-hidden', 'true' ); 70 } ); 71 27 72 /* ── SEARCH ── */ 28 73 function openSearch() { … … 31 76 if ( searchOpen ) searchOpen.setAttribute( 'aria-expanded', 'true' ); 32 77 lockScroll(); 33 setTimeout( function () { 34 var inp = searchOverlay.querySelector( 'input[type="search"]' ); 35 if ( inp ) inp.focus(); 36 }, 100 ); 78 trapOpen( searchOverlay, searchOverlay.querySelector( 'input[type="search"]' ) ); 37 79 } 38 80 function closeSearch() { 39 if ( ! searchOverlay ) return;81 if ( ! searchOverlay || ! searchOverlay.classList.contains( 'open' ) ) return; 40 82 searchOverlay.classList.remove( 'open' ); 41 83 if ( searchOpen ) searchOpen.setAttribute( 'aria-expanded', 'false' ); 42 84 unlockScroll(); 85 trapClose( searchOverlay ); 43 86 } 44 87 on( searchClose, 'click', closeSearch ); … … 70 113 if ( miniCartOpen ) miniCartOpen.setAttribute( 'aria-expanded', 'true' ); 71 114 lockScroll(); 115 trapOpen( miniCartDrawer, miniCartClose ); 72 116 } 73 117 function closeMiniCart() { 74 if ( ! miniCartDrawer ) return;118 if ( ! miniCartDrawer || ! miniCartDrawer.classList.contains( 'open' ) ) return; 75 119 miniCartDrawer.classList.remove( 'open' ); 76 120 if ( miniCartOverlay ) miniCartOverlay.classList.remove( 'open' ); 77 121 if ( miniCartOpen ) miniCartOpen.setAttribute( 'aria-expanded', 'false' ); 78 122 unlockScroll(); 123 trapClose( miniCartDrawer ); 79 124 } 80 125 window.flowixOpenMiniCart = function () { openMiniCart(); refreshMiniCart(); }; … … 89 134 if ( navHamburger ) { navHamburger.classList.add( 'active' ); navHamburger.setAttribute( 'aria-expanded', 'true' ); } 90 135 lockScroll(); 136 trapOpen( mobileDrawer, mobileClose ); 91 137 } 92 138 function closeMobileNav() { 93 if ( ! mobileDrawer ) return;139 if ( ! mobileDrawer || ! mobileDrawer.classList.contains( 'open' ) ) return; 94 140 mobileDrawer.classList.remove( 'open' ); 95 141 if ( mobileOverlay ) mobileOverlay.classList.remove( 'open' ); 96 142 if ( navHamburger ) { navHamburger.classList.remove( 'active' ); navHamburger.setAttribute( 'aria-expanded', 'false' ); } 97 143 unlockScroll(); 144 trapClose( mobileDrawer ); 98 145 } 99 146 on( mobileClose, 'click', closeMobileNav ); … … 166 213 } 167 214 168 /* ── COUNTDOWNS (homepage "Sale Countdown" sections) ── */169 ( function () {170 function pad( n ) { return ( n < 10 ? '0' : '' ) + n; }171 function tick() {172 document.querySelectorAll( '.fx-countdown[data-end]' ).forEach( function ( el ) {173 var left = parseInt( el.getAttribute( 'data-end' ), 10 ) - Date.now();174 if ( ! left || left <= 0 ) {175 if ( ! el.classList.contains( 'is-over' ) ) {176 el.classList.add( 'is-over' );177 el.textContent = el.getAttribute( 'data-after' ) || '';178 }179 return;180 }181 var s = Math.floor( left / 1000 );182 var v = { d: Math.floor( s / 86400 ), h: Math.floor( s % 86400 / 3600 ), m: Math.floor( s % 3600 / 60 ), s: s % 60 };183 el.querySelectorAll( '[data-u]' ).forEach( function ( n ) { n.textContent = pad( v[ n.getAttribute( 'data-u' ) ] ); } );184 } );185 }186 tick();187 setInterval( tick, 1000 );188 } )();189 215 190 216 /* ── SMOOTH SCROLL ANCHORS ── */ … … 206 232 e.preventDefault(); 207 233 window.scrollTo( { top: target.getBoundingClientRect().top + window.scrollY - 90, behavior: 'smooth' } ); 234 // Move keyboard focus too (skip link, in-page links). 235 if ( ! target.matches( FOCUSABLE ) && ! target.hasAttribute( 'tabindex' ) ) target.setAttribute( 'tabindex', '-1' ); 236 target.focus( { preventScroll: true } ); 208 237 } 209 238 } )(); … … 256 285 data.append( 'action', 'woocommerce_add_to_cart' ); 257 286 data.append( 'product_id', productId ); 258 data.append( 'quantity', 1 ); 287 var qtyInput = btn.parentNode && btn.parentNode.querySelector( '.sticky-atc-qty' ); 288 data.append( 'quantity', Math.max( 1, parseInt( qtyInput ? qtyInput.value : 1, 10 ) || 1 ) ); 259 289 260 290 fetch( flowixVars.ajaxUrl, { method: 'POST', body: data, credentials: 'same-origin' } ) … … 280 310 /* ── SCROLL-IN ANIMATIONS ── */ 281 311 if ( 'IntersectionObserver' in window ) { 282 var animItems = document.querySelectorAll( '.product-card, .cat-tile, .t estimonial-card, .trust-item' );312 var animItems = document.querySelectorAll( '.product-card, .cat-tile, .trust-item' ); 283 313 if ( animItems.length ) { 284 314 var styleEl = document.createElement( 'style' ); … … 294 324 } 295 325 296 /* ── DROPDOWN NAV ACCESSIBILITY ── */ 326 /* ── DROPDOWN NAV ACCESSIBILITY ── 327 Opens on hover and on keyboard focus; aria-expanded follows both. Escape closes it and 328 returns focus to the parent link; the arrow-down key opens it from the parent link. */ 297 329 document.querySelectorAll( '.primary-nav > li' ).forEach( function ( item ) { 298 330 var link = item.querySelector( 'a' ); 299 331 var submenu = item.querySelector( '.sub-menu' ); 300 332 if ( ! submenu || ! link ) return; 333 var hover = false; 334 var escaped = false; 335 function set( open ) { 336 item.classList.toggle( 'is-open', open ); 337 link.setAttribute( 'aria-expanded', open ? 'true' : 'false' ); 338 } 301 339 link.setAttribute( 'aria-haspopup', 'true' ); 302 340 link.setAttribute( 'aria-expanded', 'false' ); 303 on( item, 'mouseenter', function () { link.setAttribute( 'aria-expanded', 'true' ); } ); 304 on( item, 'mouseleave', function () { link.setAttribute( 'aria-expanded', 'false' ); } ); 341 on( item, 'mouseenter', function () { hover = true; set( true ); } ); 342 on( item, 'mouseleave', function () { hover = false; if ( ! item.contains( document.activeElement ) ) set( false ); } ); 343 on( item, 'focusin', function () { 344 if ( escaped ) { escaped = false; return; } 345 set( true ); 346 } ); 347 on( item, 'focusout', function ( e ) { 348 if ( ! item.contains( e.relatedTarget ) && ! hover ) set( false ); 349 } ); 350 on( item, 'keydown', function ( e ) { 351 if ( e.key === 'Escape' && item.classList.contains( 'is-open' ) ) { 352 e.stopPropagation(); 353 set( false ); 354 escaped = document.activeElement !== link; 355 link.focus(); 356 } else if ( e.key === 'ArrowDown' && document.activeElement === link ) { 357 var firstSub = submenu.querySelector( 'a' ); 358 if ( firstSub ) { e.preventDefault(); set( true ); firstSub.focus(); } 359 } 360 } ); 305 361 } ); 306 362 -
flowix/1.1.2/assets/js/shop-features.js
r353588 r356478 1 1 /** 2 * Flowix — Shop Features: wishlist, quick view, swatches, added-to-cart, load more, 3 * sale countdown, delivery cut-off, recently viewed, product tabs layout. 2 * Flowix — Shop Features: front-end behaviour for the shop options in the Customizer. 4 3 */ 5 4 ( function( $ ) { … … 12 11 var T = S.i18n; 13 12 14 function store( key, val ) {15 try {16 if ( val === undefined ) {17 return JSON.parse( window.localStorage.getItem( key ) || '[]' );18 }19 window.localStorage.setItem( key, JSON.stringify( val ) );20 } catch ( e ) {}21 return [];22 }23 function ids( list ) {24 return ( Array.isArray( list ) ? list : [] ).map( Number ).filter( Boolean );25 }26 13 27 28 /* ── ADD TO CART WITHOUT LEAVING THE PAGE (product page + quick view) ── */ 14 /* ── ADD TO CART WITHOUT LEAVING THE PAGE ── */ 29 15 $( document ).on( 'submit', 'form.cart', function( e ) { 30 16 var form = this; 31 17 var sub = e.originalEvent && e.originalEvent.submitter; 32 if ( ( sub && sub.name === 'flowix_buy_now' ) || form.classList.contains( 'grouped_form' ) || ! window.fetch || ! window.FormData ) { 18 var box = '.product'; 19 if ( form.classList.contains( 'grouped_form' ) || ! window.fetch || ! window.FormData ) { 33 20 return; // normal submit → checkout / WooCommerce handles it 34 21 } … … 63 50 $btn.removeClass( 'added' ); 64 51 }, 2000 ); 65 var name = $( form ).closest( '.product, .fx-qv').find( '.product_title' ).first().text();52 var name = $( form ).closest( box ).find( '.product_title' ).first().text(); 66 53 $( document.body ).trigger( 'wc_fragment_refresh' ); 67 54 document.body.dispatchEvent( new CustomEvent( 'added_to_cart' ) ); 68 afterAdd( name, $( form ).closest( '.product, .fx-qv').find( 'img' ).first().attr( 'src' ) );55 afterAdd( name, $( form ).closest( box ).find( 'img' ).first().attr( 'src' ) ); 69 56 } ) 70 57 .catch( function() { -
flowix/1.1.2/footer.php
r353748 r356478 1 1 </main><!-- #main-content --> 2 2 3 <!-- Newsletter section --> 4 <?php if ( function_exists( 'flowix_newsletter_default' ) && get_theme_mod( 'flowix_show_newsletter', true ) ) : ?> 5 <section class="newsletter-section" id="newsletter" aria-labelledby="newsletter-heading"> 6 <div class="container"> 7 <span class="eyebrow"><?php esc_html_e( 'Stay in the loop', 'flowix' ); ?></span> 8 <h2 id="newsletter-heading" style="font-size:1.6rem;margin-bottom:6px;"><?php echo esc_html( get_theme_mod( 'flowix_newsletter_heading', flowix_newsletter_default( 'heading' ) ) ); ?></h2> 9 <p class="lead"><?php echo esc_html( get_theme_mod( 'flowix_newsletter_subtext', flowix_newsletter_default( 'subtext' ) ) ); ?></p> 10 <?php if ( isset( $_GET['flowix_subscribed'] ) ) : // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- display only. ?> 11 <p class="fx-newsletter-thanks" role="status"><?php esc_html_e( "Thanks — you're on the list.", 'flowix' ); ?></p> 12 <?php endif; ?> 13 <form class="newsletter-form" method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 14 <input type="hidden" name="action" value="flowix_newsletter"> 15 <?php wp_nonce_field( 'flowix_newsletter', 'newsletter_nonce' ); ?> 16 <label for="newsletter-email" class="screen-reader-text"><?php esc_html_e( 'Email address', 'flowix' ); ?></label> 17 <input type="email" id="newsletter-email" name="email" placeholder="<?php esc_attr_e( 'your@email.co.za', 'flowix' ); ?>" required> 18 <button type="submit" class="btn btn--gold"><?php esc_html_e( 'Subscribe', 'flowix' ); ?></button> 19 </form> 20 </div> 21 </section> 22 <?php endif; ?> 3 <?php do_action( 'flowix_before_footer' ); ?> 23 4 24 5 <?php flowix_render_footer(); ?> 25 26 </div><!-- #page -->27 6 28 7 <?php wp_footer(); ?> -
flowix/1.1.2/functions.php
r353748 r356478 5 5 * 6 6 * @package Flowix 7 * @version 1. 0.57 * @version 1.1.2 8 8 */ 9 9 … … 11 11 12 12 // Version constant 13 define( 'FLOWIX_VERSION', '1.1. 0' );13 define( 'FLOWIX_VERSION', '1.1.2' ); 14 14 define( 'FLOWIX_DIR', get_template_directory() ); 15 15 define( 'FLOWIX_URI', get_template_directory_uri() ); 16 17 /** 18 * Field type for the builders' longer text (taglines, text items, address). 19 */ 20 function flowix_multiline_field() { 21 return 'line'; 22 } 23 24 /** 25 * Escape theme-built HTML on output: everything WordPress allows in post content, plus the 26 * inline SVG icons, buttons and form fields the header, footer and shop parts use. 27 * 28 * @param string $html HTML built by the theme. 29 * @return string 30 */ 31 function flowix_kses( $html ) { 32 static $allowed = null; 33 if ( null === $allowed ) { 34 $allowed = wp_kses_allowed_html( 'post' ); 35 $common = [ 'class' => true, 'id' => true, 'style' => true, 'title' => true, 'role' => true, 'tabindex' => true, 'hidden' => true, 36 'aria-label' => true, 'aria-hidden' => true, 'aria-expanded' => true, 'aria-controls' => true, 'aria-haspopup' => true, 37 'aria-current' => true, 'aria-live' => true, 'aria-labelledby' => true, 'aria-describedby' => true, 'data-*' => true ]; 38 $svg = [ 'xmlns' => true, 'width' => true, 'height' => true, 'viewbox' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 39 'stroke-linecap' => true, 'stroke-linejoin' => true, 'focusable' => true, 'd' => true, 'cx' => true, 'cy' => true, 'r' => true, 'rx' => true, 40 'ry' => true, 'x' => true, 'y' => true, 'x1' => true, 'y1' => true, 'x2' => true, 'y2' => true, 'points' => true, 'transform' => true, 41 'opacity' => true, 'fill-rule' => true, 'clip-rule' => true ]; 42 foreach ( [ 'svg', 'path', 'circle', 'line', 'polyline', 'polygon', 'rect', 'ellipse', 'g' ] as $tag ) { 43 $allowed[ $tag ] = $common + $svg; 44 } 45 $allowed['button'] = $common + [ 'type' => true, 'name' => true, 'value' => true, 'disabled' => true ]; 46 $allowed['form'] = $common + [ 'action' => true, 'method' => true, 'role' => true ]; 47 $allowed['input'] = $common + [ 'type' => true, 'name' => true, 'value' => true, 'placeholder' => true, 'required' => true, 'checked' => true, 48 'min' => true, 'max' => true, 'step' => true, 'autocomplete' => true ]; 49 $allowed['label'] = $common + [ 'for' => true ]; 50 $allowed['select'] = $common + [ 'name' => true ]; 51 $allowed['option'] = [ 'value' => true, 'selected' => true ]; 52 foreach ( $allowed as $tag => $attrs ) { 53 if ( is_array( $attrs ) ) { 54 $allowed[ $tag ] += $common; 55 } 56 } 57 } 58 return wp_kses( (string) $html, $allowed ); 59 } 16 60 17 61 /* ================================================================ … … 132 176 add_action( 'wp_enqueue_scripts', 'flowix_enqueue_scripts' ); 133 177 134 /* ================================================================ 135 CRITICAL CSS — INLINE ABOVE THE FOLD 136 Outputs minimal render-critical styles directly in <head> so the 137 page paints immediately without waiting for the full stylesheet. 178 /** 179 * Right-to-left sites (Arabic, Hebrew, Persian…): swap each Flowix stylesheet for its mirrored 180 * "-rtl.css" twin. The twins are made by tools/build.py. 181 */ 182 function flowix_rtl_styles() { 183 if ( ! is_rtl() ) { 184 return; 185 } 186 foreach ( wp_styles()->registered as $handle => $style ) { 187 if ( 0 !== strpos( $handle, 'flowix-' ) || ! is_string( $style->src ) || 0 !== strpos( $style->src, FLOWIX_URI ) ) { 188 continue; 189 } 190 $rel = ltrim( substr( strtok( $style->src, '?' ), strlen( FLOWIX_URI ) ), '/' ); 191 if ( '.css' === substr( $rel, -4 ) && file_exists( FLOWIX_DIR . '/' . substr( $rel, 0, -4 ) . '-rtl.css' ) ) { 192 wp_style_add_data( $handle, 'rtl', 'replace' ); 193 } 194 } 195 } 196 add_action( 'wp_enqueue_scripts', 'flowix_rtl_styles', 999 ); 197 add_action( 'customize_controls_enqueue_scripts', 'flowix_rtl_styles', 999 ); 198 add_action( 'enqueue_block_editor_assets', 'flowix_rtl_styles', 999 ); 199 add_action( 'admin_enqueue_scripts', 'flowix_rtl_styles', 999 ); 200 201 /* ================================================================ 202 CRITICAL CSS — the first stylesheet in <head>: page colours and the 203 hidden skip link, so the page paints right before the full stylesheet. 138 204 ================================================================ */ 139 205 function flowix_critical_css() { 140 $bc = flowix_brand()['colors']; 141 $accent = $bc['accent']; 142 $body_bg = $bc['background']; 143 $body_text= $bc['text']; 144 echo '<style id="flowix-critical">*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}' 206 $bc = flowix_brand()['colors']; 207 $css = '*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}' 145 208 . 'html{font-size:' . absint( get_theme_mod( 'flowix_font_size_base', 16 ) ) . 'px}' 146 . 'body{background:' . esc_attr( $body_bg ) . ';color:' . esc_attr( $body_text) . ';font-family:system-ui,sans-serif;line-height:1.65}'147 . '#masthead{background:' . esc_attr( $bc['header'] ) . ';position:relative;z-index:100}'148 . '.site-title a{color:' . esc_attr( $bc['header_text'] ) . '}'149 . '.hero{background:' . esc_attr( get_theme_mod( 'flowix_hero_bg_color' ) ?: $bc['hero'] ) . '}'209 . 'body{background:' . sanitize_hex_color( $bc['background'] ) . ';color:' . sanitize_hex_color( $bc['text'] ) . ';font-family:system-ui,sans-serif;line-height:1.65}' 210 . '#masthead{background:' . sanitize_hex_color( $bc['header'] ) . ';position:relative;z-index:100}' 211 . '.site-title a{color:' . sanitize_hex_color( $bc['header_text'] ) . '}' 212 . '.hero{background:' . sanitize_hex_color( get_theme_mod( 'flowix_hero_bg_color' ) ?: $bc['hero'] ) . '}' 150 213 . '.skip-link{position:absolute;top:-9999px;left:-9999px}' 151 . '.skip-link:focus{top:0;left:0;z-index:9999;background:' . esc_attr( $accent ) . ';color:#fff;padding:8px 16px}' 152 . '</style>' . "\n"; 153 } 154 add_action( 'wp_head', 'flowix_critical_css', 1 ); 155 156 /* ================================================================ 157 FONT DISPLAY SWAP — ensure fonts don't block rendering 158 ================================================================ */ 159 function flowix_font_display_swap( $tag, $handle ) { 160 if ( 'flowix-fonts' !== $handle ) return $tag; 161 // Add font-display=swap to the Google Fonts URL if not already present 162 if ( strpos( $tag, 'display=swap' ) === false ) { 163 $tag = str_replace( "rel='stylesheet'", "rel='stylesheet' media='print' onload=\"this.media='all'\"", $tag ); 164 } 165 return $tag; 166 } 167 add_filter( 'style_loader_tag', 'flowix_font_display_swap', 10, 2 ); 214 . '.skip-link:focus{top:0;left:0;z-index:9999;background:' . sanitize_hex_color( $bc['accent'] ) . ';color:#fff;padding:8px 16px}'; 215 wp_register_style( 'flowix-critical', false, [], FLOWIX_VERSION ); 216 wp_enqueue_style( 'flowix-critical' ); 217 wp_add_inline_style( 'flowix-critical', $css ); 218 } 219 add_action( 'wp_enqueue_scripts', 'flowix_critical_css', 1 ); 220 221 /** 222 * Connect to Google Fonts early (the Brand Kit fonts come from there). 223 * 224 * @param array $urls URLs to print for resource hints. 225 * @param string $relation_type The relation type the URLs are printed for. 226 * @return array 227 */ 228 function flowix_resource_hints( $urls, $relation_type ) { 229 if ( 'preconnect' === $relation_type && wp_style_is( 'flowix-fonts', 'queue' ) ) { 230 $urls[] = 'https://fonts.googleapis.com'; 231 $urls[] = [ 232 'href' => 'https://fonts.gstatic.com', 233 'crossorigin', 234 ]; 235 } 236 return $urls; 237 } 238 add_filter( 'wp_resource_hints', 'flowix_resource_hints', 10, 2 ); 168 239 169 240 /* ================================================================ … … 255 326 if ( ! class_exists( 'WooCommerce' ) || ! WC()->cart ) return $fragments; 256 327 $count = WC()->cart->get_cart_contents_count(); 257 $fragments['.cart-item-count'] = '<span class="cart-item-count">' . $count. '</span>';258 $fragments['.mini-cart-count'] = '<span class="mini-cart-count">' . $count. '</span>';328 $fragments['.cart-item-count'] = '<span class="cart-item-count">' . absint( $count ) . '</span>'; 329 $fragments['.mini-cart-count'] = '<span class="mini-cart-count">' . absint( $count ) . '</span>'; 259 330 return $fragments; 260 331 } … … 265 336 ================================================================ */ 266 337 function flowix_loop_columns() { 267 return get_theme_mod( 'flowix_shop_columns', 4);338 return absint( get_theme_mod( 'flowix_shop_columns', 4 ) ); 268 339 } 269 340 add_filter( 'loop_shop_columns', 'flowix_loop_columns' ); … … 273 344 ================================================================ */ 274 345 function flowix_products_per_page() { 275 return get_theme_mod( 'flowix_products_per_page', 12);346 return absint( get_theme_mod( 'flowix_products_per_page', 12 ) ); 276 347 } 277 348 add_filter( 'loop_shop_per_page', 'flowix_products_per_page' ); … … 386 457 $image_id = $product->get_image_id(); 387 458 $image_url = $image_id 388 ? wp_get_attachment_image_url( $image_id, ' flowix-product-card' )389 : wc_placeholder_img_src( ' flowix-product-card' );390 $image_alt = get_post_meta( $image_id, '_wp_attachment_image_alt', true ) ?: get_the_title( $product->get_id() );459 ? wp_get_attachment_image_url( $image_id, 'woocommerce_thumbnail' ) 460 : wc_placeholder_img_src( 'woocommerce_thumbnail' ); 461 $image_alt = $image_id ? (string) get_post_meta( $image_id, '_wp_attachment_image_alt', true ) : ''; 391 462 $price_html = $product->get_price_html(); 392 463 $on_sale = $product->is_on_sale(); 393 $is_new = ( strtotime( $product->get_date_created() ) > strtotime( '-30 days' ) );464 $is_new = $product->get_date_created() && $product->get_date_created()->getTimestamp() > time() - 30 * DAY_IN_SECONDS; 394 465 $cats = get_the_terms( $product->get_id(), 'product_cat' ); 395 466 $cat_name = ( $cats && ! is_wp_error( $cats ) ) ? reset( $cats )->name : ''; … … 398 469 $atc_text = $product->is_type( 'simple' ) ? __( 'Add to Cart', 'flowix' ) : __( 'View Product', 'flowix' ); 399 470 ?> 400 <article class="product-card" itemscope itemtype="https://schema.org/Product">471 <article class="product-card"> 401 472 <div class="product-card-media"> 402 473 <a href="<?php echo esc_url( get_permalink( $product->get_id() ) ); ?>" aria-label="<?php echo esc_attr( $product->get_name() ); ?>"> … … 404 475 alt="<?php echo esc_attr( $image_alt ); ?>" 405 476 width="480" height="480" 406 loading="lazy" 407 itemprop="image"> 477 loading="lazy"> 408 478 </a> 409 479 <?php 410 480 if ( function_exists( 'flowix_card_overlay' ) ) { 411 echo flowix_ card_overlay( $product ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- built from escaped parts.481 echo flowix_kses( flowix_card_overlay( $product ) ); 412 482 } else { 413 483 ?> … … 425 495 <?php echo esc_html( $atc_text ); ?> 426 496 </a> 427 <a href="<?php echo esc_url( get_permalink( $product->get_id() ) ); ?>" class="btn btn--sm " style="background:rgba(255,255,255,0.1);border-color:rgba(255,255,255,0.2);color:#fff">497 <a href="<?php echo esc_url( get_permalink( $product->get_id() ) ); ?>" class="btn btn--sm btn--ghost-light"> 428 498 <?php esc_html_e( 'Details', 'flowix' ); ?> 429 499 </a> … … 432 502 <div class="product-card-body"> 433 503 <?php if ( $cat_name ) : ?> 434 <span class="product-card-category" itemprop="category"><?php echo esc_html( $cat_name ); ?></span>504 <span class="product-card-category"><?php echo esc_html( $cat_name ); ?></span> 435 505 <?php endif; ?> 436 <h3 class="product-card-title" itemprop="name">437 <a href="<?php echo esc_url( get_permalink( $product->get_id() ) ); ?>" itemprop="url">506 <h3 class="product-card-title"> 507 <a href="<?php echo esc_url( get_permalink( $product->get_id() ) ); ?>"> 438 508 <?php echo esc_html( $product->get_name() ); ?> 439 509 </a> 440 510 </h3> 441 <div class="product-card-price" itemprop="offers" itemscope itemtype="https://schema.org/Offer">511 <div class="product-card-price"> 442 512 <?php echo wp_kses_post( $price_html ); ?> 443 <meta itemprop="priceCurrency" content="<?php echo esc_attr( get_woocommerce_currency() ); ?>">444 513 </div> 445 <?php echo function_exists( 'flowix_card_swatches' ) ? flowix_card_swatches( $product ) : ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped?>514 <?php do_action( 'flowix_product_card_after_price', $product ); ?> 446 515 </div> 447 516 </article> … … 470 539 471 540 /* ================================================================ 472 PAGE TEMPLATES 473 ================================================================ */ 474 function flowix_page_templates( $templates ) { 475 $templates['templates/page-full-width.php'] = __( 'Full Width', 'flowix' ); 476 $templates['templates/page-no-header.php'] = __( 'Landing Page (No Header/Footer)', 'flowix' ); 477 return $templates; 478 } 479 add_filter( 'theme_page_templates', 'flowix_page_templates' ); 541 SKIP LINK — printed before the admin toolbar (which core adds to 542 wp_body_open at priority 0), so it is always the first thing to tab to. 543 ================================================================ */ 544 function flowix_skip_link() { 545 echo '<a class="skip-link" href="#main-content">' . esc_html__( 'Skip to content', 'flowix' ) . '</a>'; 546 } 547 add_action( 'wp_body_open', 'flowix_skip_link', -1 ); 480 548 481 549 /* ================================================================ 482 550 EXCERPT LENGTH & MORE 483 551 ================================================================ */ 484 add_filter( 'excerpt_length', fn() => 22 ); 485 add_filter( 'excerpt_more', fn() => '…' ); 486 487 /* ================================================================ 488 REMOVE WOOCOMMERCE DEFAULT STYLES (we provide our own) 489 ================================================================ */ 490 function flowix_dequeue_woo_styles( $enqueue_styles ) { 491 // Keep core WC styles, remove block-based ones we don't need 492 // unset( $enqueue_styles['wc-block-style'] ); 493 return $enqueue_styles; 494 } 495 add_filter( 'woocommerce_enqueue_styles', 'flowix_dequeue_woo_styles' ); 496 497 require_once FLOWIX_DIR . '/inc/upsell.php'; 552 add_filter( 'excerpt_length', fn( $length ) => is_admin() ? $length : 22 ); 553 add_filter( 'excerpt_more', fn( $more ) => is_admin() ? $more : '…' ); 554 498 555 499 556 /* ================================================================ … … 509 566 require_once FLOWIX_DIR . '/inc/page-options.php'; 510 567 require_once FLOWIX_DIR . '/inc/patterns.php'; 568 require_once FLOWIX_DIR . '/inc/editor-history.php'; 511 569 require_once FLOWIX_DIR . '/inc/header-builder.php'; 512 570 require_once FLOWIX_DIR . '/inc/footer-builder.php'; -
flowix/1.1.2/header.php
r353588 r356478 5 5 <meta name="viewport" content="width=device-width, initial-scale=1"> 6 6 <meta name="theme-color" content="<?php echo esc_attr( flowix_brand()['colors']['header'] ); ?>"> 7 <?php if ( get_theme_mod( 'flowix_font_heading', '' ) || get_theme_mod( 'flowix_font_body', '' ) ) : ?>8 <link rel="preconnect" href="https://fonts.googleapis.com">9 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>10 <?php endif; ?>11 7 <?php wp_head(); ?> 12 8 </head> 13 9 <body <?php body_class(); ?>> 14 10 <?php wp_body_open(); ?> 15 16 <a class="skip-link" href="#main-content"><?php esc_html_e( 'Skip to content', 'flowix' ); ?></a>17 11 18 12 <!-- Back to top --> … … 78 72 <nav class="mobile-nav-drawer" id="mobile-nav-drawer" aria-label="<?php esc_attr_e( 'Mobile Navigation', 'flowix' ); ?>"> 79 73 <div class="mobile-nav-header"> 80 <span class="site-title" style="color:var(--cream);font-size:1.2rem;">74 <span class="site-title"> 81 75 <?php bloginfo( 'name' ); ?> 82 76 </span> … … 96 90 ?> 97 91 <?php if ( class_exists( 'WooCommerce' ) ) : ?> 98 <div style="padding:20px 24px;border-top:1px solid rgba(155,123,58,0.2);margin-top:auto;">99 <a href="<?php echo esc_url( wc_get_cart_url() ); ?>" class="btn btn--gold " style="width:100%;text-align:center;display:block;">92 <div class="mobile-nav-footer"> 93 <a href="<?php echo esc_url( wc_get_cart_url() ); ?>" class="btn btn--gold btn--block"> 100 94 <?php esc_html_e( 'View Cart', 'flowix' ); ?> 101 95 </a> … … 108 102 <!-- Breadcrumbs (non-homepage) --> 109 103 <?php if ( ! is_front_page() && ! is_home() ) : ?> 110 <div class="breadcrumbs-bar" aria-label="<?php esc_attr_e( 'Breadcrumb', 'flowix' ); ?>">104 <div class="breadcrumbs-bar"> 111 105 <div class="container"> 112 106 <?php … … 114 108 woocommerce_breadcrumb( [ 115 109 'delimiter' => ' / ', 116 'wrap_before' => '<nav class="woocommerce-breadcrumb" aria-label=" Breadcrumb">',110 'wrap_before' => '<nav class="woocommerce-breadcrumb" aria-label="' . esc_attr__( 'Breadcrumb', 'flowix' ) . '">', 117 111 'wrap_after' => '</nav>', 118 112 ] ); 119 113 } else { 120 echo '<nav aria-label="Breadcrumb">'; 121 echo '<a href="' . esc_url( home_url() ) . '">' . esc_html__( 'Home', 'flowix' ) . '</a>'; 122 echo ' / <span>' . esc_html( get_the_title() ) . '</span>'; 114 if ( is_search() ) { 115 /* translators: %s: search query */ 116 $flowix_crumb = sprintf( __( 'Search results for "%s"', 'flowix' ), get_search_query( false ) ); 117 } elseif ( is_404() ) { 118 $flowix_crumb = __( 'Page not found', 'flowix' ); 119 } elseif ( is_archive() ) { 120 $flowix_crumb = wp_strip_all_tags( get_the_archive_title() ); 121 } else { 122 $flowix_crumb = get_the_title(); 123 } 124 echo '<nav aria-label="' . esc_attr__( 'Breadcrumb', 'flowix' ) . '">'; 125 echo '<a href="' . esc_url( home_url( '/' ) ) . '">' . esc_html__( 'Home', 'flowix' ) . '</a>'; 126 echo ' / <span>' . esc_html( $flowix_crumb ) . '</span>'; 123 127 echo '</nav>'; 124 128 } -
flowix/1.1.2/inc/brand-kit.php
r353588 r356478 72 72 function flowix_brand() { 73 73 static $cache = []; 74 $ctx = function_exists( 'flowix_context_look_key' ) ? flowix_context_look_key() :'';74 $ctx = ''; 75 75 $ctx = $ctx && ! did_action( 'flowix_look_ready' ) ? '' : $ctx; 76 76 if ( ! isset( $cache[ $ctx ] ) || is_customize_preview() ) { … … 291 291 '--btn-ink' => $p['text'], 292 292 '--btn-ink-text' => $p['bg'], 293 // Older light background tokens still used by a few sections (countdown, call-to-action, newsletter…).293 // Older light background tokens still used by a few sections. 294 294 '--linen' => $p['surface'], 295 295 '--parchment' => $p['surface'], … … 334 334 } 335 335 $float = ( 'toggle' === $mode && $dark['float'] ) ? 'true' : 'false'; 336 echo '<script id="flowix-dark-init">(function(){var m=' . wp_json_encode( $mode ) . ',d=document.documentElement,s=null;if(' . esc_js( $float ) . ')d.classList.add("fx-float-on");try{s=localStorage.getItem("flowix-theme")}catch(e){}var sys=window.matchMedia&&matchMedia("(prefers-color-scheme: dark)").matches;var on=m==="always"||(m==="auto"&&sys)||(m==="toggle"&&(s?s==="dark":sys));if(on)d.classList.add("fx-dark");d.setAttribute("data-flowix-dark",m);})();</script>' . "\n"; 336 wp_print_inline_script_tag( 337 '(function(){var m=' . wp_json_encode( $mode ) . ',d=document.documentElement,s=null;if(' . $float . ')d.classList.add("fx-float-on");try{s=localStorage.getItem("flowix-theme")}catch(e){}var sys=window.matchMedia&&matchMedia("(prefers-color-scheme: dark)").matches;var on=m==="always"||(m==="auto"&&sys)||(m==="toggle"&&(s?s==="dark":sys));if(on)d.classList.add("fx-dark");d.setAttribute("data-flowix-dark",m);})();', 338 [ 'id' => 'flowix-dark-init' ] 339 ); 337 340 } 338 341 add_action( 'wp_head', 'flowix_dark_head_script', 1 ); … … 342 345 $d = flowix_brand()['dark']; 343 346 if ( ( 'toggle' === $d['mode'] && $d['float'] ) || is_customize_preview() ) { 344 echo '<button type="button" class="fx-dark-toggle fx-dark-float" data-flowix-dark-toggle aria-label="' . esc_attr__( 'Switch between light and dark mode', 'flowix' ) . '">' . flowix_inline_icon( 'moon', 18 ) . flowix_inline_icon( 'sun', 18 ) . '</button>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped347 echo flowix_kses( '<button type="button" class="fx-dark-toggle fx-dark-float" data-flowix-dark-toggle aria-label="' . esc_attr__( 'Switch between light and dark mode', 'flowix' ) . '">' . flowix_inline_icon( 'moon', 18 ) . flowix_inline_icon( 'sun', 18 ) . '</button>' ); 345 348 } 346 349 } ); … … 351 354 } 352 355 353 /** Printed after the old global colour CSS (and before element styles) so the brand wins. */ 354 add_action( 'wp_head', function() { 355 echo '<style id="flowix-brand">' . flowix_brand_css( flowix_brand() ) . "</style>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- built from sanitized values. 356 }, 998 ); 356 /** Added after the other theme stylesheets (and before the element styles) so the brand wins. */ 357 function flowix_enqueue_brand_css() { 358 wp_register_style( 'flowix-brand', false, [], FLOWIX_VERSION ); 359 wp_enqueue_style( 'flowix-brand' ); 360 wp_add_inline_style( 'flowix-brand', wp_strip_all_tags( flowix_brand_css( flowix_brand() ) ) ); 361 } 362 add_action( 'wp_enqueue_scripts', 'flowix_enqueue_brand_css', 1000 ); 357 363 358 364 /* ================================================================ -
flowix/1.1.2/inc/customizer-output.php
r353588 r356478 168 168 [ 'flowix_cats', '#categories-section', 'var(--bg-page, var(--cream))', 60, 60 ], 169 169 [ 'flowix_feat', '#featured-section', 'var(--bg-warm, var(--cream-warm))', 60, 60 ], 170 [ 'flowix_testi', '#testimonials-section', 'var(--bg-page, var(--cream))', 60, 60 ],171 [ 'flowix_vendor','.vendor-cta', 'var(--ink)', 80, 80 ],172 [ 'flowix_news', '.newsletter-section', 'var(--hr-bg)', 56, 56 ],173 170 [ 'flowix_footer_sec', '#colophon', 'var(--ft-bg)', 60, 40 ], 174 171 ]; -
flowix/1.1.2/inc/customizer.php
r353748 r356478 11 11 HELPER: PER-SECTION BACKGROUND CONTROLS 12 12 ================================================================ */ 13 /** 14 * Select / radio settings: keep the value only if it is one of the control's choices. 15 * 16 * @param mixed $value Submitted value. 17 * @param WP_Customize_Setting $setting The setting. 18 */ 19 function flowix_sanitize_choice( $value, $setting ) { 20 $control = $setting->manager->get_control( $setting->id ); 21 $choices = $control ? (array) $control->choices : []; 22 return array_key_exists( (string) $value, $choices ) || array_key_exists( (int) $value, $choices ) ? $value : $setting->default; 23 } 24 13 25 function flowix_register_bg_controls( $wp_customize, $section_id, $prefix, $default_color = '#FAF7F2', $default_pt = 60, $default_pb = 60 ) { 14 26 … … 71 83 72 84 $add_select = function( $id, $section, $label, $default, $choices, $transport = 'postMessage' ) use ( $wp_customize ) { 73 $wp_customize->add_setting( $id, [ 'default' => $default, 'sanitize_callback' => ' sanitize_text_field', 'transport' => $transport ] );85 $wp_customize->add_setting( $id, [ 'default' => $default, 'sanitize_callback' => 'flowix_sanitize_choice', 'transport' => $transport ] ); 74 86 $wp_customize->add_control( $id, [ 'section' => $section, 'label' => $label, 'type' => 'select', 'choices' => $choices ] ); 75 87 }; … … 133 145 $wp_customize->add_control( 'flowix_featured_count', [ 'section' => 'flowix_featured', 'label' => __( 'Number of Products', 'flowix' ), 'type' => 'number', 'input_attrs' => [ 'min' => 2, 'max' => 16, 'step' => 2 ] ] ); 134 146 135 /* ══════════════════════════════════════════════════════136 TESTIMONIALS137 ══════════════════════════════════════════════════════ */138 $wp_customize->add_section( 'flowix_testimonials', [ 'panel' => 'flowix_panel', 'title' => __( 'Testimonials', 'flowix' ), 'priority' => 80 ] );139 $add_text( 'flowix_testimonials_eyebrow', 'flowix_testimonials', __( 'Eyebrow Text', 'flowix' ), __( 'Happy Customers', 'flowix' ) );140 $add_text( 'flowix_testimonials_heading', 'flowix_testimonials', __( 'Heading', 'flowix' ), __( 'What People Say', 'flowix' ) );141 $t = [ 1 => [ 'Incredible quality and such fast delivery!', 'Thandi M.', 'Johannesburg' ], 2 => [ 'Finally a SA marketplace that actually works.', 'Ruan B.', 'Cape Town' ], 3 => [ "I found products I could never find elsewhere.", 'Priya N.', 'Durban' ] ];142 for ( $i = 1; $i <= 3; $i++ ) {143 /* translators: %d: review number */144 $add_text( "flowix_t{$i}_quote", 'flowix_testimonials', sprintf( __( 'Review %d — Quote', 'flowix' ), $i ), $t[$i][0], 'textarea' );145 /* translators: %d: review number */146 $add_text( "flowix_t{$i}_name", 'flowix_testimonials', sprintf( __( 'Review %d — Name', 'flowix' ), $i ), $t[$i][1] );147 /* translators: %d: review number */148 $add_text( "flowix_t{$i}_loc", 'flowix_testimonials', sprintf( __( 'Review %d — Location', 'flowix' ), $i ), $t[$i][2] );149 }150 flowix_register_bg_controls( $wp_customize, 'flowix_testimonials', 'flowix_testi', '', 60, 60 );151 147 152 /* ══════════════════════════════════════════════════════153 VENDOR CTA154 ══════════════════════════════════════════════════════ */155 $wp_customize->add_section( 'flowix_vendor_cta', [ 'panel' => 'flowix_panel', 'title' => __( 'Vendor CTA Section', 'flowix' ), 'priority' => 90 ] );156 $add_text( 'flowix_vendor_eyebrow', 'flowix_vendor_cta', __( 'Eyebrow Text', 'flowix' ), __( 'Grow Your Business', 'flowix' ) );157 $add_text( 'flowix_vendor_heading', 'flowix_vendor_cta', __( 'Heading', 'flowix' ), __( 'Sell on Flowix', 'flowix' ) );158 $add_text( 'flowix_vendor_paragraph', 'flowix_vendor_cta', __( 'Paragraph', 'flowix' ), __( 'Join our growing marketplace. List your products and reach thousands of shoppers.', 'flowix' ), 'textarea' );159 $add_text( 'flowix_vendor_btn1_text', 'flowix_vendor_cta', __( 'Button 1 Label', 'flowix' ), __( 'Start Selling', 'flowix' ) );160 $add_text( 'flowix_vendor_btn1_url', 'flowix_vendor_cta', __( 'Button 1 URL', 'flowix' ), home_url( '/become-a-vendor' ), 'url', 'refresh' );161 $add_text( 'flowix_vendor_btn2_text', 'flowix_vendor_cta', __( 'Button 2 Label', 'flowix' ), __( 'Learn More', 'flowix' ) );162 $add_text( 'flowix_vendor_btn2_url', 'flowix_vendor_cta', __( 'Button 2 URL', 'flowix' ), home_url( '/about' ), 'url', 'refresh' );163 flowix_register_bg_controls( $wp_customize, 'flowix_vendor_cta', 'flowix_vendor', '', 80, 80 );164 165 /* ══════════════════════════════════════════════════════166 NEWSLETTER167 ══════════════════════════════════════════════════════ */168 148 169 149 /* ══════════════════════════════════════════════════════ … … 171 151 ══════════════════════════════════════════════════════ */ 172 152 $wp_customize->add_section( 'flowix_shop', [ 'active_callback' => 'flowix_has_shop', 'panel' => 'flowix_panel', 'title' => __( 'Shop Settings', 'flowix' ), 'priority' => 100 ] ); 173 $wp_customize->add_setting( 'flowix_shop_columns', [ 'default' => 4, 'sanitize_callback' => ' absint', 'transport' => 'refresh' ] );153 $wp_customize->add_setting( 'flowix_shop_columns', [ 'default' => 4, 'sanitize_callback' => 'flowix_sanitize_choice', 'transport' => 'refresh' ] ); 174 154 $wp_customize->add_control( 'flowix_shop_columns', [ 'section' => 'flowix_shop', 'label' => __( 'Product Columns', 'flowix' ), 'type' => 'select', 'choices' => [ 2=>'2', 3=>'3', 4=>'4', 5=>'5' ] ] ); 175 155 $wp_customize->add_setting( 'flowix_products_per_page', [ 'default' => 12, 'sanitize_callback' => 'absint', 'transport' => 'refresh' ] ); … … 181 161 ══════════════════════════════════════════════════════ */ 182 162 $wp_customize->add_section( 'flowix_footer', [ 'panel' => 'flowix_panel', 'title' => __( 'Footer', 'flowix' ), 'priority' => 110 ] ); 183 $add_text( 'flowix_footer_tagline', 'flowix_footer', __( 'Brand Tagline', 'flowix' ), flowix_footer_tagline_default(), 'textarea' ); 184 $add_text( 'flowix_footer_copyright', 'flowix_footer', __( 'Copyright Text', 'flowix' ), gmdate( 'Y' ) . ' Flowix. All rights reserved.' ); 163 // The tagline and copyright line are set on their items in Flowix — Layout Builder → Footer Builder. 185 164 $add_text( 'flowix_social_facebook', 'flowix_footer', __( 'Facebook URL', 'flowix' ), '', 'url', 'refresh' ); 186 165 $add_text( 'flowix_social_instagram', 'flowix_footer', __( 'Instagram URL', 'flowix' ), '', 'url', 'refresh' ); -
flowix/1.1.2/inc/design-gallery.php
r353588 r356478 66 66 67 67 function flowix_look_default_home() { 68 return [ 'hero', 'trust', 'categories', 'featured', 'testimonials', 'vendor' ];68 return wp_list_pluck( flowix_home_sections_default(), 'type' ); 69 69 } 70 70 … … 101 101 ], 102 102 'home' => [ 'hero', 'b_stats', 'featured', 'categories', 'text_image', 'testimonials' ], 103 'footer' => [ 'main' => [ 'grid' => '1fr 1fr 1fr 1fr', 'cols' => [ [ 'brand' ], [ 'links_shop' ], [ 'links_info' ], [ 'newsletter'] ] ], 'bottom' => [ 'l' => [ 'copyright' ], 'c' => [], 'r' => [ 'backtotop' ] ] ],103 'footer' => [ 'main' => [ 'grid' => '1fr 1fr 1fr 1fr', 'cols' => [ [ 'brand' ], [ 'links_shop' ], [ 'links_info' ], [ flowix_footer_signup_item() ] ] ], 'bottom' => [ 'l' => [ 'copyright' ], 'c' => [], 'r' => [ 'backtotop' ] ] ], 104 104 'elements' => flowix_look_base( 0, 0, [ 105 105 'hero' => [ 'bg' => $tok( 'hero' ), 'tex' => 'graph', 'texc' => '#111111', 'texo' => 7, 'texs' => 40, 'bw' => [ 't' => 0, 'r' => 0, 'b' => 2, 'l' => 0, 'u' => 'px' ], 'bs' => 'solid', 'bc' => '#111111' ], … … 176 176 177 177 178 /* The gallery's looks for the Customizer scripts (Premium adds saved looks on top). */178 /* The gallery's looks for the Customizer scripts. */ 179 179 add_filter( 'flowix_element_js_data', function( $data ) { 180 180 if ( ! isset( $data['looks'] ) ) { -
flowix/1.1.2/inc/element-registry.php
r353588 r356478 210 210 'feat_section' => [ __( 'Featured Section', 'flowix' ), '#featured-section', $b ], 211 211 'feat_heading' => [ __( 'Featured Heading', 'flowix' ), '#featured-heading', $tb ], 212 ],213 ],214 'testimonials' => [215 'title' => __( 'Homepage — Testimonials', 'flowix' ),216 'elements' => [217 'testi_section' => [ __( 'Testimonials Section', 'flowix' ), '#testimonials-section', $b ],218 'testi_grid' => [ __( 'Testimonials Grid', 'flowix' ), '.testimonials-grid', $b ],219 'testi_card' => [ __( 'Testimonial Card', 'flowix' ), '.testimonial-card', $b ],220 'testi_stars' => [ __( 'Stars', 'flowix' ), '.testimonial-stars', $tb ],221 'testi_quote' => [ __( 'Quote Text', 'flowix' ), '.testimonial-quote', $tb ],222 'testi_avatar' => [ __( 'Avatar Circle', 'flowix' ), '.testimonial-avatar', $tb ],223 'testi_name' => [ __( 'Customer Name', 'flowix' ), '.testimonial-name', $tb ],224 'testi_meta' => [ __( 'Customer Location', 'flowix' ), '.testimonial-meta', $tb ],225 ],226 ],227 'vendor' => [228 'title' => __( 'Homepage — Vendor Call-to-Action', 'flowix' ),229 'elements' => [230 'vendor_section' => [ __( 'Vendor Section', 'flowix' ), '.vendor-cta', $b ],231 'vendor_inner' => [ __( 'Vendor Content Box', 'flowix' ), '.vendor-cta-inner', $b ],232 'vendor_eyebrow' => [ __( 'Vendor Eyebrow', 'flowix' ), '.vendor-cta .eyebrow', $tb ],233 'vendor_heading' => [ __( 'Vendor Heading', 'flowix' ), '.vendor-cta h2', $tb ],234 'vendor_text' => [ __( 'Vendor Paragraph', 'flowix' ), '.vendor-cta p', $tb ],235 'vendor_btn1' => [ __( 'Vendor Button 1', 'flowix' ), '.vendor-cta-actions .btn:nth-child(1)', $tb ],236 'vendor_btn2' => [ __( 'Vendor Button 2', 'flowix' ), '.vendor-cta-actions .btn:nth-child(2)', $tb ],237 ],238 ],239 'newsletter' => [240 'title' => __( 'Newsletter Signup', 'flowix' ),241 'elements' => [242 'news_section' => [ __( 'Newsletter Section', 'flowix' ), '.newsletter-section', $b ],243 'news_eyebrow' => [ __( 'Newsletter Eyebrow', 'flowix' ), '.newsletter-section .eyebrow', $tb ],244 'news_heading' => [ __( 'Newsletter Heading', 'flowix' ), '#newsletter-heading', $tb ],245 'news_text' => [ __( 'Newsletter Text', 'flowix' ), '.newsletter-section .lead', $tb ],246 'news_form' => [ __( 'Signup Form', 'flowix' ), '.newsletter-form', $b ],247 'news_input' => [ __( 'Email Box', 'flowix' ), '.newsletter-form input[type="email"]', $tb ],248 'news_btn' => [ __( 'Subscribe Button', 'flowix' ), '.newsletter-form .btn', $tb ],249 212 ], 250 213 ], -
flowix/1.1.2/inc/element-styles.php
r353588 r356478 231 231 static $icons = null; 232 232 if ( null === $icons ) { 233 $data = json_decode( (string) file_get_contents( FLOWIX_DIR . '/assets/icons/lucide.json' ), true ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- local theme file.233 $data = wp_json_file_decode( FLOWIX_DIR . '/assets/icons/lucide.json', [ 'associative' => true ] ); 234 234 $icons = $data['icons'] ?? []; 235 235 } … … 278 278 /** System fonts that need no download. */ 279 279 function flowix_system_fonts() { 280 return[280 $fonts = [ 281 281 'System UI' => 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif', 282 282 'Arial' => 'Arial, Helvetica, sans-serif', … … 288 288 'Times New Roman' => '"Times New Roman", Times, serif', 289 289 'Courier New' => '"Courier New", Courier, monospace', 290 ] + ( function_exists( 'flowix_custom_font_stacks' ) ? flowix_custom_font_stacks() : [] ); 290 ]; 291 return $fonts; 291 292 } 292 293 … … 724 725 725 726 /** 726 * Print element CSS after all other theme styles so it always wins, plus the motion list. 727 * In the Customizer preview each element gets its own <style> tag so live edits can replace it. 728 */ 729 function flowix_print_element_css() { 727 * The element CSS for this page view. 728 * In the Customizer preview each element keeps its own CSS so live edits can replace it. 729 */ 730 function flowix_page_element_css_data() { 731 static $memo = null; 732 if ( null !== $memo ) { 733 return $memo; 734 } 730 735 if ( is_customize_preview() ) { 731 flowix_print_element_css_build(); 732 return; 733 } 734 // Built output is cached until any design setting (or the page's own look) changes. 735 $key = 'flowix_css_' . flowix_css_fingerprint(); 736 $cached = get_transient( $key ); 737 if ( false === $cached ) { 738 ob_start(); 739 flowix_print_element_css_build(); 740 $cached = ob_get_clean(); 741 set_transient( $key, $cached, WEEK_IN_SECONDS ); 742 } 743 echo $cached; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- built from sanitized values. 744 } 745 746 /** Everything the element CSS depends on. */ 747 function flowix_css_fingerprint() { 748 $mods = []; 749 foreach ( (array) get_theme_mods() as $k => $v ) { 750 if ( 0 === strpos( (string) $k, 'flowix_' ) ) { 751 $mods[ $k ] = $v; 752 } 753 } 754 return md5( wp_json_encode( [ 755 $mods, 756 function_exists( 'flowix_context_look_key' ) ? flowix_context_look_key() : '', 757 get_option( 'flowix_saved_styles' ), 758 get_bloginfo( 'name' ), 759 FLOWIX_VERSION, 760 filemtime( __FILE__ ), 761 filemtime( FLOWIX_DIR . '/inc/design-gallery.php' ), 762 ] ) ); 763 } 764 765 766 function flowix_print_element_css_build() { 767 $preview = is_customize_preview(); 768 $css = ''; 769 $motion = []; 736 $memo = flowix_element_css_data( true ); 737 return $memo; 738 } 739 $memo = flowix_element_css_data( false ); 740 return $memo; 741 } 742 743 744 /** 745 * The element CSS (and motion / site effect settings) as plain data, so it can be cached and 746 * then escaped as it is printed. 747 * 748 * @param bool $per_element Keep each element's CSS separate (Customizer preview). 749 */ 750 function flowix_element_css_data( $per_element ) { 751 $data = [ 'css' => '', 'blocks' => [], 'motion' => [], 'site_css' => '', 'site_cfg' => [] ]; 770 752 foreach ( flowix_styled_elements() as $id => $el ) { 771 753 $block = flowix_build_element_css( $el['sel'], $el['style'] ); 772 if ( $p review) {773 echo '<style id="flowix-el-' . esc_attr( $id ) . '">' . $block . "</style>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- built from sanitized values.754 if ( $per_element ) { 755 $data['blocks'][ $id ] = $block; 774 756 } else { 775 $css .= $block; 776 } 777 } 778 if ( $css ) { 779 echo '<style id="flowix-element-styles">' . $css . "</style>\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- built from sanitized values. 780 } 781 } 782 add_action( 'wp_head', 'flowix_print_element_css', 999 ); 757 $data['css'] .= $block; 758 } 759 } 760 return $data; 761 } 762 763 /** 764 * Element CSS goes in last, after every other theme stylesheet (and the Brand Kit), so it always wins. 765 * In the Customizer preview each element gets its own stylesheet handle ("flowix-el-{id}"), which the 766 * preview script rewrites while you edit. 767 */ 768 function flowix_enqueue_element_css() { 769 $data = flowix_page_element_css_data(); 770 foreach ( (array) ( $data['blocks'] ?? [] ) as $id => $block ) { 771 $handle = 'flowix-el-' . sanitize_key( $id ); 772 wp_register_style( $handle, false, [ 'flowix-brand' ], FLOWIX_VERSION ); 773 wp_enqueue_style( $handle ); 774 wp_add_inline_style( $handle, wp_strip_all_tags( (string) $block ) ); 775 } 776 if ( ! empty( $data['css'] ) ) { 777 wp_register_style( 'flowix-element-styles', false, [ 'flowix-brand' ], FLOWIX_VERSION ); 778 wp_enqueue_style( 'flowix-element-styles' ); 779 wp_add_inline_style( 'flowix-element-styles', wp_strip_all_tags( (string) $data['css'] ) ); 780 } 781 } 782 add_action( 'wp_enqueue_scripts', 'flowix_enqueue_element_css', 1001 ); 783 783 784 784 785 … … 797 798 wp_send_json_error( null, 403 ); 798 799 } 799 $items = json_decode( wp_unslash( $_POST['items'] ?? '{}' ), true ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- each value sanitized below. 800 // JSON of element styles; every value is cleaned again by flowix_clean_style() / flowix_brand_clean() below. 801 $items = json_decode( isset( $_POST['items'] ) ? sanitize_textarea_field( wp_unslash( $_POST['items'] ) ) : '{}', true ); 800 802 $flat = flowix_registry_flat(); 801 803 $out = []; … … 1009 1011 'everyOdd' => __( 'Every other one (1st, 3rd, 5th…)', 'flowix' ), 1010 1012 'everyEven' => __( 'Every other one (2nd, 4th, 6th…)', 'flowix' ), 1013 'transparent' => __( 'transparent', 'flowix' ), 1014 // Sample words on the Design Gallery look thumbnails. 1015 'lookShop' => __( 'Shop', 'flowix' ), 1016 'lookTagline' => __( 'Live better.', 'flowix' ), 1017 'lookButton' => __( 'Shop now', 'flowix' ), 1011 1018 ], 1012 1019 ] ); -
flowix/1.1.2/inc/footer-builder.php
r353748 r356478 18 18 $title = [ 'title' => [ 'text', __( 'Heading', 'flowix' ) ] ]; 19 19 return [ 20 'brand' => [ 'label' => __( 'Logo + Tagline', 'flowix' ), 'icon' => 'image', 'fields' => [ 'tag' => [ 'textarea', __( 'Tagline', 'flowix' ) ] ] ],20 'brand' => [ 'label' => __( 'Logo + Tagline', 'flowix' ), 'icon' => 'image', 'fields' => [ 'tag' => [ flowix_multiline_field(), __( 'Tagline', 'flowix' ) ] ] ], 21 21 'social' => [ 'label' => __( 'Social Icons', 'flowix' ), 'icon' => 'share-2', 'fields' => $title ], 22 22 'links_shop' => [ 'label' => __( 'Shop Links (auto)', 'flowix' ), 'icon' => 'store', 'fields' => $title + [ 'n' => [ 'select', __( 'Categories to list', 'flowix' ), [ '3' => '3', '5' => '5', '8' => '8', '12' => '12' ] ] ] ], … … 30 30 'widgets3' => [ 'label' => __( 'Widget Area: Footer — Account', 'flowix' ), 'icon' => 'layout-grid' ], 31 31 'widgets4' => [ 'label' => __( 'Widget Area: Footer — Info', 'flowix' ), 'icon' => 'layout-grid' ], 32 'contact' => [ 'label' => __( 'Contact Details', 'flowix' ), 'icon' => 'map-pin', 'fields' => $title + [ 'addr' => [ 'textarea', __( 'Address', 'flowix' ) ], 'phone' => [ 'text', __( 'Phone', 'flowix' ) ], 'email' => [ 'text', __( 'Email', 'flowix' ) ], 'hours' => [ 'text', __( 'Opening hours', 'flowix' ) ] ] ],32 'contact' => [ 'label' => __( 'Contact Details', 'flowix' ), 'icon' => 'map-pin', 'fields' => $title + [ 'addr' => [ flowix_multiline_field(), __( 'Address', 'flowix' ) ], 'phone' => [ 'text', __( 'Phone', 'flowix' ) ], 'email' => [ 'text', __( 'Email', 'flowix' ) ], 'hours' => [ 'text', __( 'Opening hours', 'flowix' ) ] ] ], 33 33 'payments' => [ 'label' => __( 'Payment Badges', 'flowix' ), 'icon' => 'credit-card', 'fields' => [ 'list' => [ 'text', __( 'Badges (comma separated)', 'flowix' ) ] ] ], 34 34 'copyright' => [ 'label' => __( 'Copyright', 'flowix' ), 'icon' => 'copyright', 'fields' => [ 'text' => [ 'text', __( 'Text — {year} and {site} are filled in for you', 'flowix' ) ] ] ], 35 'text1' => [ 'label' => __( 'Text / HTML 1', 'flowix' ), 'icon' => 'type', 'fields' => $title + [ 'html' => [ 'textarea', __( 'Text (links and bold allowed)', 'flowix' ) ] ] ],36 'text2' => [ 'label' => __( 'Text / HTML 2', 'flowix' ), 'icon' => 'type', 'fields' => $title + [ 'html' => [ 'textarea', __( 'Text (links and bold allowed)', 'flowix' ) ] ] ],35 'text1' => [ 'label' => __( 'Text / HTML 1', 'flowix' ), 'icon' => 'type', 'fields' => $title + [ 'html' => [ flowix_multiline_field(), __( 'Text (links and bold allowed)', 'flowix' ) ] ] ], 36 'text2' => [ 'label' => __( 'Text / HTML 2', 'flowix' ), 'icon' => 'type', 'fields' => $title + [ 'html' => [ flowix_multiline_field(), __( 'Text (links and bold allowed)', 'flowix' ) ] ] ], 37 37 'button' => [ 'label' => __( 'Button', 'flowix' ), 'icon' => 'mouse-pointer-click', 'fields' => [ 't' => [ 'text', __( 'Label', 'flowix' ) ], 'u' => [ 'url', __( 'Link', 'flowix' ) ] ] ], 38 38 'backtotop' => [ 'label' => __( 'Back to Top Link', 'flowix' ), 'icon' => 'arrow-up' ], … … 61 61 } 62 62 63 /** Class for a column layout preset: "1.4fr 1fr" → "fx-fgrid--1p4fr-1fr" (rules in header-builder.css). */ 64 function flowix_footer_grid_class( $grid ) { 65 return 'fx-fgrid--' . str_replace( [ '.', ' ' ], [ 'p', '-' ], $grid ); 66 } 67 63 68 /** Footer tagline before the owner writes their own: shop wording with WooCommerce, else the site tagline. */ 64 69 function flowix_footer_tagline_default() { 65 70 return class_exists( 'WooCommerce' ) ? __( 'Your South African marketplace for quality goods, delivered to your door.', 'flowix' ) : get_bloginfo( 'description' ); 71 } 72 73 /** Footer item the design gallery looks put in their sign-up column. */ 74 function flowix_footer_signup_item() { 75 return 'contact'; 66 76 } 67 77 … … 81 91 'links_account' => [ 'title' => __( 'My Account', 'flowix' ) ], 82 92 'links_info' => [ 'title' => __( 'Information', 'flowix' ) ], 83 'newsletter' => [ 'title' => __( 'Stay in the loop', 'flowix' ), 'text' => $shop ? __( 'New arrivals and deals, straight to your inbox.', 'flowix' ) : __( 'New posts and news, straight to your inbox.', 'flowix' ) ],84 93 'contact' => [ 'title' => __( 'Get in touch', 'flowix' ) ], 85 94 'payments' => [ 'list' => 'PayFast, Visa, Mastercard, EFT' ], … … 127 136 'grid' => $grid, 128 137 'cols' => $cols, 129 'mcols' => in_array( (string) ( $m['mcols'] ?? '2' ), [ '1', '2' ], true ) ? (string) $m['mcols']: '2',138 'mcols' => '1' === (string) ( $m['mcols'] ?? '2' ) ? '1' : '2', 130 139 ], 131 140 ]; … … 146 155 break; 147 156 case 'textarea': 157 case 'line': 148 158 $v = wp_kses( (string) $v, [ 'a' => [ 'href' => true, 'target' => true, 'rel' => true ], 'strong' => [], 'b' => [], 'em' => [], 'i' => [], 'br' => [], 'span' => [ 'class' => true ] ] ); 149 159 break; … … 309 319 function flowix_render_footer() { 310 320 $cfg = flowix_footer_config(); 311 echo '<footer id="colophon" class="fx-footer" role="contentinfo" itemscope itemtype="https://schema.org/WPFooter">';321 echo '<footer id="colophon" class="fx-footer" role="contentinfo">'; 312 322 313 323 if ( $cfg['main']['on'] ) { … … 320 330 $cols .= '<div class="footer-col fx-fcol fx-fcol--' . ( $i + 1 ) . '">' . $html . '</div>'; 321 331 } 322 echo '<div class="footer-main fx-fmain fx-fmain--m' . esc_attr( $cfg['main']['mcols'] ) . '" style="--fx-fgrid: ' . esc_attr( $cfg['main']['grid'] ) . '">' . $cols . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- items escape their own output.332 echo flowix_kses( '<div class="footer-main fx-fmain fx-fmain--m' . esc_attr( $cfg['main']['mcols'] ) . ' ' . esc_attr( flowix_footer_grid_class( $cfg['main']['grid'] ) ) . '">' . $cols . '</div>' ); 323 333 } 324 334 … … 336 346 } 337 347 if ( $any ) { 338 echo '<div class="footer-bottom fx-fbottom">' . $zones . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- items escape their own output.348 echo flowix_kses( '<div class="footer-bottom fx-fbottom">' . $zones . '</div>' ); 339 349 } 340 350 } -
flowix/1.1.2/inc/header-builder.php
r353748 r356478 36 36 'button' => [ 'label' => __( 'Button 1', 'flowix' ), 'icon' => 'mouse-pointer-click', 'fields' => [ 't' => [ 'text', __( 'Label', 'flowix' ) ], 'u' => [ 'url', __( 'Link', 'flowix' ) ], 's' => [ 'select', __( 'Style', 'flowix' ), [ 'gold' => __( 'Accent', 'flowix' ), 'primary' => __( 'Dark', 'flowix' ), 'ghost' => __( 'Outline', 'flowix' ) ] ], 'n' => [ 'check', __( 'Open in a new tab', 'flowix' ) ] ] ], 37 37 'button2' => [ 'label' => __( 'Button 2', 'flowix' ), 'icon' => 'mouse-pointer-click', 'fields' => [ 't' => [ 'text', __( 'Label', 'flowix' ) ], 'u' => [ 'url', __( 'Link', 'flowix' ) ], 's' => [ 'select', __( 'Style', 'flowix' ), [ 'gold' => __( 'Accent', 'flowix' ), 'primary' => __( 'Dark', 'flowix' ), 'ghost' => __( 'Outline', 'flowix' ) ] ], 'n' => [ 'check', __( 'Open in a new tab', 'flowix' ) ] ] ], 38 'text' => [ 'label' => __( 'Text / HTML 1', 'flowix' ), 'icon' => 'type', 'fields' => [ 'h' => [ 'textarea', __( 'Text (links and bold allowed)', 'flowix' ) ] ] ],39 'text2' => [ 'label' => __( 'Text / HTML 2', 'flowix' ), 'icon' => 'type', 'fields' => [ 'h' => [ 'textarea', __( 'Text (links and bold allowed)', 'flowix' ) ] ] ],38 'text' => [ 'label' => __( 'Text / HTML 1', 'flowix' ), 'icon' => 'type', 'fields' => [ 'h' => [ flowix_multiline_field(), __( 'Text (links and bold allowed)', 'flowix' ) ] ] ], 39 'text2' => [ 'label' => __( 'Text / HTML 2', 'flowix' ), 'icon' => 'type', 'fields' => [ 'h' => [ flowix_multiline_field(), __( 'Text (links and bold allowed)', 'flowix' ) ] ] ], 40 40 'phone' => [ 'label' => __( 'Phone Number', 'flowix' ), 'icon' => 'phone', 'fields' => [ 'v' => [ 'text', __( 'Phone number', 'flowix' ) ] ] ], 41 41 'email' => [ 'label' => __( 'Email Address', 'flowix' ), 'icon' => 'mail', 'fields' => [ 'v' => [ 'text', __( 'Email address', 'flowix' ) ] ] ], … … 119 119 break; 120 120 case 'textarea': 121 case 'line': 121 122 $v = wp_kses( (string) $v, [ 'a' => [ 'href' => true, 'target' => true, 'rel' => true ], 'strong' => [], 'b' => [], 'em' => [], 'i' => [], 'br' => [], 'span' => [ 'class' => true ] ] ); 122 123 break; … … 240 241 } 241 242 $count = WC()->cart ? WC()->cart->get_cart_contents_count() : 0; 242 return '<button type="button" class="header-util-btn fx-hitem fx-hitem--cart" data-flowix-open="cart" aria-label="' . esc_attr__( 'Open cart', 'flowix' ) . '" aria-expanded="false" aria-controls="mini-cart-drawer" style="position:relative;">' . flowix_inline_icon( 'shopping-bag', 17 ) . '<span class="cart-item-count" aria-live="polite">' . esc_html( $count ) . '</span></button>';243 return '<button type="button" class="header-util-btn fx-hitem fx-hitem--cart" data-flowix-open="cart" aria-label="' . esc_attr__( 'Open cart', 'flowix' ) . '" aria-expanded="false" aria-controls="mini-cart-drawer">' . flowix_inline_icon( 'shopping-bag', 17 ) . '<span class="cart-item-count" aria-live="polite">' . esc_html( $count ) . '</span></button>'; 243 244 244 245 case 'cart_total': … … 334 335 $echo = is_array( $args ) ? ( $args['echo'] ?? true ) : ( is_object( $args ) ? ( $args->echo ?? true ) : true ); 335 336 if ( $echo ) { 336 echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped above.337 echo flowix_kses( $html ); 337 338 return ''; 338 339 } … … 377 378 } 378 379 echo '<div class="fx-hrow fx-hrow--' . esc_attr( $row ) . ' fx-' . esc_attr( $dev ) . ' ' . esc_attr( $row_class[ $row ][0] ) . '">'; 379 echo '<div class="fx-hrow-inner ' . esc_attr( $row_class[ $row ][1] ) . '">' . implode( '', $zones ) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- items escape their own output.380 echo flowix_kses( '<div class="fx-hrow-inner ' . esc_attr( $row_class[ $row ][1] ) . '">' . implode( '', $zones ) . '</div>' ); 380 381 echo '</div>'; 381 382 } … … 451 452 'mobile' => __( 'Mobile', 'flowix' ), 452 453 'rows' => [ 'top' => __( 'Top Bar', 'flowix' ), 'main' => __( 'Main Row', 'flowix' ), 'bottom' => __( 'Bottom Row', 'flowix' ) ], 453 'zones' => [ 'l' => __( 'Left', 'flowix' ), 'c' => __( 'Centre', 'flowix' ), 'r' => __( 'Right', 'flowix' ) ],454 'zones' => is_rtl() ? [ 'l' => __( 'Right', 'flowix' ), 'c' => __( 'Centre', 'flowix' ), 'r' => __( 'Left', 'flowix' ) ] : [ 'l' => __( 'Left', 'flowix' ), 'c' => __( 'Centre', 'flowix' ), 'r' => __( 'Right', 'flowix' ) ], // 'l' is the start side: on the right for right-to-left sites. 454 455 'available' => __( 'Available items — drag into a row', 'flowix' ), 455 456 'dragHint' => __( 'Drag items between the boxes. Click an item to change its settings.', 'flowix' ), -
flowix/1.1.2/inc/home-sections.php
r353748 r356478 19 19 ================================================================ */ 20 20 21 /** Icons offered for feature boxes. */22 function flowix_feature_icons() {23 $names = [ 'truck', 'shield-check', 'credit-card', 'rotate-ccw', 'headphones', 'gift', 'heart', 'star', 'leaf', 'award', 'clock', 'map-pin', 'package', 'sparkles', 'thumbs-up', 'lock', 'percent', 'tag', 'zap', 'globe', 'smile', 'recycle', 'hand-heart', 'badge-check', 'store', 'phone', 'mail', 'coffee', 'sun', 'flower-2' ];24 return array_combine( $names, array_map( fn( $n ) => ucwords( str_replace( '-', ' ', $n ) ), $names ) );25 }26 21 27 22 /** … … 37 32 'url' => [ 'url', __( 'Button link', 'flowix' ) ], 38 33 ]; 39 $sources = [40 'featured' => __( 'Featured products', 'flowix' ),41 'sale' => __( 'On sale', 'flowix' ),42 'new' => __( 'Newest', 'flowix' ),43 'best' => __( 'Best sellers', 'flowix' ),44 'rated' => __( 'Top rated', 'flowix' ),45 'category' => __( 'From one category', 'flowix' ),46 ];47 $cats = [ '' => __( '— Choose —', 'flowix' ) ];48 if ( taxonomy_exists( 'product_cat' ) ) {49 $terms = get_terms( [ 'taxonomy' => 'product_cat', 'hide_empty' => false ] );50 if ( ! is_wp_error( $terms ) ) {51 foreach ( $terms as $t ) {52 $cats[ $t->slug ] = $t->name;53 }54 }55 }56 34 $post_cats = [ '' => __( 'All categories', 'flowix' ) ]; 57 35 foreach ( get_categories( [ 'hide_empty' => false ] ) as $t ) { 58 36 $post_cats[ $t->slug ] = $t->name; 59 37 } 60 $icons = flowix_feature_icons();61 $features = [ 'eyebrow' => [ 'text', __( 'Small label above', 'flowix' ) ], 'heading' => [ 'text', __( 'Heading', 'flowix' ) ] ];62 for ( $i = 1; $i <= 4; $i++ ) {63 /* translators: %d: feature number */64 $features[ "i{$i}" ] = [ 'icon', sprintf( __( 'Feature %d — icon', 'flowix' ), $i ), $icons ];65 /* translators: %d: feature number */66 $features[ "t{$i}" ] = [ 'text', sprintf( __( 'Feature %d — title', 'flowix' ), $i ) ];67 /* translators: %d: feature number */68 $features[ "x{$i}" ] = [ 'textarea', sprintf( __( 'Feature %d — text', 'flowix' ), $i ) ];69 }70 71 38 return [ 72 39 // Built-in sections (one each) … … 76 43 'categories' => [ 'label' => __( 'Categories', 'flowix' ), 'icon' => 'layout-grid', 'builtin' => true, 'woo' => true, 'note' => __( 'Edit its text under Flowix Theme → Categories Section.', 'flowix' ) ], 77 44 'featured' => [ 'label' => __( 'Featured Products', 'flowix' ), 'icon' => 'star', 'builtin' => true, 'woo' => true, 'note' => __( 'Edit its text under Flowix Theme → Featured Products.', 'flowix' ) ], 78 'testimonials' => [ 'label' => __( 'Testimonials', 'flowix' ), 'icon' => 'quote', 'builtin' => true, 'note' => __( 'Edit the reviews under Flowix Theme → Testimonials.', 'flowix' ) ],79 'vendor' => [ 'label' => __( 'Vendor Call-to-Action', 'flowix' ), 'icon' => 'store', 'builtin' => true, 'note' => __( 'Edit its text under Flowix Theme → Vendor CTA Section.', 'flowix' ) ],80 81 45 // Add as many as you like 82 'banner' => [ 'label' => __( 'Image Banner', 'flowix' ), 'icon' => 'image', 'fields' => [83 'image' => [ 'image', __( 'Background image', 'flowix' ) ],84 'eyebrow' => [ 'text', __( 'Small label above', 'flowix' ) ],85 'heading' => [ 'text', __( 'Heading', 'flowix' ) ],86 'text' => [ 'textarea', __( 'Text', 'flowix' ) ],87 ] + $btn + [88 'height' => [ 'select', __( 'Height', 'flowix' ), [ 'm' => __( 'Medium', 'flowix' ), 's' => __( 'Short', 'flowix' ), 'l' => __( 'Tall', 'flowix' ), 'full' => __( 'Full screen', 'flowix' ) ] ],89 'align' => [ 'select', __( 'Text position', 'flowix' ), [ 'center' => __( 'Centre', 'flowix' ), 'left' => __( 'Left', 'flowix' ), 'right' => __( 'Right', 'flowix' ) ] ],90 ] ],91 46 'posts' => [ 'label' => __( 'Latest Posts', 'flowix' ), 'icon' => 'newspaper', 'fields' => [ 92 47 'eyebrow' => [ 'text', __( 'Small label above', 'flowix' ) ], … … 95 50 'count' => [ 'number', __( 'How many', 'flowix' ), 1, 12 ], 96 51 ] + $btn ], 97 'text_image' => [ 'label' => __( 'Image + Text', 'flowix' ), 'icon' => 'columns-2', 'fields' => [98 'image' => [ 'image', __( 'Image', 'flowix' ) ],99 'side' => [ 'select', __( 'Image side', 'flowix' ), [ 'left' => __( 'Left', 'flowix' ), 'right' => __( 'Right', 'flowix' ) ] ],100 'eyebrow' => [ 'text', __( 'Small label above', 'flowix' ) ],101 'heading' => [ 'text', __( 'Heading', 'flowix' ) ],102 'text' => [ 'textarea', __( 'Text', 'flowix' ) ],103 ] + $btn ],104 'features' => [ 'label' => __( 'Feature Icons', 'flowix' ), 'icon' => 'sparkles', 'fields' => $features ],105 'cta' => [ 'label' => __( 'Call to Action', 'flowix' ), 'icon' => 'megaphone', 'fields' => [106 'eyebrow' => [ 'text', __( 'Small label above', 'flowix' ) ],107 'heading' => [ 'text', __( 'Heading', 'flowix' ) ],108 'text' => [ 'textarea', __( 'Text', 'flowix' ) ],109 ] + $btn + [110 'btn2' => [ 'text', __( 'Second button label', 'flowix' ) ],111 'url2' => [ 'url', __( 'Second button link', 'flowix' ) ],112 ] ],113 52 'spacer' => [ 'label' => __( 'Spacer', 'flowix' ), 'icon' => 'separator-horizontal', 'fields' => [ 114 'h' => [ 'number', __( 'Height (px )', 'flowix' ), 0, 600 ],53 'h' => [ 'number', __( 'Height (px, in steps of 10)', 'flowix' ), 0, 600 ], 115 54 ] ], 116 55 ]; … … 124 63 function flowix_section_defaults_base( $type ) { 125 64 $d = [ 126 'banner' => [ 'eyebrow' => __( 'New season', 'flowix' ), 'heading' => __( 'Summer Collection', 'flowix' ), 'text' => __( 'Fresh styles, made for sunny days.', 'flowix' ), 'btn' => __( 'Shop the collection', 'flowix' ), 'url' => '/shop/', 'height' => 'm', 'align' => 'center' ],127 65 'posts' => [ 'eyebrow' => __( 'From the blog', 'flowix' ), 'heading' => __( 'Latest posts', 'flowix' ), 'cat' => '', 'count' => 3 ], 128 'text_image' => [ 'side' => 'left', 'eyebrow' => __( 'Our story', 'flowix' ), 'heading' => __( 'Made with care', 'flowix' ), 'text' => __( 'Tell your customers what makes you different — where your products come from and why you love them.', 'flowix' ), 'btn' => __( 'Learn more', 'flowix' ), 'url' => '/about/' ],129 'features' => [ 'heading' => __( 'Why shop with us', 'flowix' ), 'i1' => 'truck', 't1' => __( 'Fast delivery', 'flowix' ), 'x1' => __( 'Nationwide in 2–4 days.', 'flowix' ), 'i2' => 'shield-check', 't2' => __( 'Secure checkout', 'flowix' ), 'x2' => __( 'Your payment details stay safe.', 'flowix' ), 'i3' => 'rotate-ccw', 't3' => __( 'Easy returns', 'flowix' ), 'x3' => __( '30 days to change your mind.', 'flowix' ), 'i4' => 'headphones', 't4' => __( 'Real support', 'flowix' ), 'x4' => __( 'Talk to a person, not a bot.', 'flowix' ) ],130 'cta' => [ 'eyebrow' => __( 'Limited offer', 'flowix' ), 'heading' => __( 'Get 10% off your first order', 'flowix' ), 'text' => __( 'Sign up today and save on everything.', 'flowix' ), 'btn' => __( 'Claim my discount', 'flowix' ), 'url' => '/my-account/' ],131 66 'spacer' => [ 'h' => 60 ], 132 67 ]; … … 144 79 return $list; 145 80 } 146 foreach ( [ 'hero', 'page_content', 'trust', 'categories', 'featured', 'testimonials', 'vendor' ] as $t ) { 81 $builtin = [ 'hero', 'page_content', 'trust', 'categories', 'featured' ]; 82 foreach ( $builtin as $t ) { 147 83 $list[] = [ 'id' => $t, 'type' => $t, 'on' => true, 'd' => (object) [] ]; 148 84 } … … 178 114 } 179 115 $seen[ $type ] = true; 180 if ( ! empty( $t['block'] ) ) {181 $out[] = [ 'id' => $id, 'type' => $type, 'on' => ! empty( $sec['on'] ), 'd' => (object) flowix_block_clean( $t['block'], (array) ( $sec['d'] ?? [] ) ) ];182 continue;183 }184 116 $data = []; 185 117 $raw = (array) ( $sec['d'] ?? [] ); … … 248 180 if ( ! empty( $types[ $sec['type'] ]['builtin'] ) ) { 249 181 get_template_part( 'template-parts/home/' . $sec['type'] ); 250 } elseif ( ! empty( $types[ $sec['type'] ]['block'] ) ) {251 echo '<section class="fx-sec fx-sec--block section fx-sec-' . esc_attr( $sec['id'] ) . '"><div class="container">' . flowix_render_block( $types[ $sec['type'] ]['block'], (array) $sec['d'], 'fxs-' . $sec['id'] ) . '</div></section>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- block output is escaped.252 182 } else { 253 183 flowix_render_section( $sec ); … … 282 212 283 213 switch ( $type ) { 284 case 'banner':285 $img = ! empty( $d['image']['id'] ) ? wp_get_attachment_url( $d['image']['id'] ) : '';286 echo '<section class="' . esc_attr( $cls . ' fx-banner--' . ( $d['height'] ?? 'm' ) . ' fx-banner--' . ( $d['align'] ?? 'center' ) ) . '"' . ( $img ? ' style="--fx-banner-img:url(\'' . esc_url( $img ) . '\')"' : '' ) . '>';287 echo '<div class="container fx-banner-inner"><div class="fx-banner-content">';288 if ( ! empty( $d['eyebrow'] ) ) {289 echo '<span class="eyebrow fx-banner-eyebrow">' . esc_html( $d['eyebrow'] ) . '</span>';290 }291 if ( ! empty( $d['heading'] ) ) {292 echo '<h2 class="fx-banner-title">' . esc_html( $d['heading'] ) . '</h2>';293 }294 if ( ! empty( $d['text'] ) ) {295 echo '<div class="fx-banner-text">' . wp_kses_post( wpautop( $d['text'] ) ) . '</div>';296 }297 echo flowix_section_button( $d['btn'] ?? '', $d['url'] ?? '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped298 echo '</div></div></section>';299 break;300 301 214 case 'posts': 302 215 $q = new WP_Query( [ … … 308 221 ] ); 309 222 echo '<section id="' . esc_attr( 'fx-' . $sec['id'] ) . '" class="' . esc_attr( $cls . ' section' ) . '"><div class="container">'; 310 echo flowix_ section_heading( $d ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped223 echo flowix_kses( flowix_section_heading( $d ) ); 311 224 if ( $q->have_posts() ) { 312 225 echo '<div class="products-grid fx-posts-grid">'; … … 321 234 } 322 235 if ( ! empty( $d['btn'] ) ) { 323 echo '<div class="fx-sec-actions">' . flowix_section_button( $d['btn'], $d['url'] ?? '', 'btn btn--primary btn--lg' ) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped236 echo flowix_kses( '<div class="fx-sec-actions">' . flowix_section_button( $d['btn'], $d['url'] ?? '', 'btn btn--primary btn--lg' ) . '</div>' ); 324 237 } 325 238 echo '</div></section>'; 326 239 break; 327 240 328 case 'text_image':329 $img = ! empty( $d['image']['id'] ) ? wp_get_attachment_image( $d['image']['id'], 'large', false, [ 'class' => 'fx-ti-img' ] ) : '<div class="fx-ti-img fx-ti-placeholder"></div>';330 echo '<section class="' . esc_attr( $cls . ' section fx-ti--' . ( $d['side'] ?? 'left' ) ) . '"><div class="container fx-ti-inner">';331 echo '<div class="fx-ti-media">' . $img . '</div><div class="fx-ti-content">'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped332 echo flowix_section_heading( $d, false ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped333 if ( ! empty( $d['text'] ) ) {334 echo '<div class="fx-ti-text">' . wp_kses_post( wpautop( $d['text'] ) ) . '</div>';335 }336 echo flowix_section_button( $d['btn'] ?? '', $d['url'] ?? '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped337 echo '</div></div></section>';338 break;339 340 case 'features':341 echo '<section class="' . esc_attr( $cls . ' section' ) . '"><div class="container">';342 echo flowix_section_heading( $d ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped343 echo '<div class="fx-features">';344 for ( $i = 1; $i <= 4; $i++ ) {345 if ( empty( $d[ "t{$i}" ] ) && empty( $d[ "x{$i}" ] ) ) {346 continue;347 }348 echo '<div class="fx-feature">';349 if ( ! empty( $d[ "i{$i}" ] ) ) {350 echo '<span class="fx-feature-icon">' . flowix_inline_icon( $d[ "i{$i}" ], 28, 1.6 ) . '</span>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped351 }352 if ( ! empty( $d[ "t{$i}" ] ) ) {353 echo '<h3 class="fx-feature-title">' . esc_html( $d[ "t{$i}" ] ) . '</h3>';354 }355 if ( ! empty( $d[ "x{$i}" ] ) ) {356 echo '<p class="fx-feature-text">' . wp_kses_post( $d[ "x{$i}" ] ) . '</p>';357 }358 echo '</div>';359 }360 echo '</div></div></section>';361 break;362 363 case 'cta':364 echo '<section class="' . esc_attr( $cls . ' section' ) . '"><div class="container fx-cta-inner">';365 echo flowix_section_heading( $d ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped366 if ( ! empty( $d['text'] ) ) {367 echo '<div class="fx-cta-text">' . wp_kses_post( wpautop( $d['text'] ) ) . '</div>';368 }369 echo '<div class="fx-sec-actions">' . flowix_section_button( $d['btn'] ?? '', $d['url'] ?? '' ) . flowix_section_button( $d['btn2'] ?? '', $d['url2'] ?? '', 'btn btn--primary btn--lg' ) . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped370 echo '</div></section>';371 break;372 373 241 case 'spacer': 374 echo '<div class="' . esc_attr( $cls ) . '" style="height:' . (int) ( $d['h'] ?? 60 ) . 'px" aria-hidden="true"></div>'; 242 $h = (int) round( max( 0, min( 600, (int) ( $d['h'] ?? 60 ) ) ) / 10 ) * 10; 243 echo '<div class="' . esc_attr( $cls . ' fx-spacer--' . $h ) . '" aria-hidden="true"></div>'; 375 244 break; 376 245 } … … 391 260 'sec_title' => [ __( 'Added Section Headings', 'flowix' ), '.fx-sec-title', $tb ], 392 261 'sec_btn' => [ __( 'Added Section Buttons', 'flowix' ), '.fx-sec-btn', $tb ], 393 'banner' => [ __( 'Image Banners', 'flowix' ), '.fx-sec--banner', $b ],394 'banner_content' => [ __( 'Image Banner — Text Box', 'flowix' ), '.fx-banner-content', $b ],395 'banner_title' => [ __( 'Image Banner — Heading', 'flowix' ), '.fx-banner-title', $tb ],396 'banner_text' => [ __( 'Image Banner — Text', 'flowix' ), '.fx-banner-text', $tb ],397 262 'posts_sec' => [ __( 'Latest Posts Sections', 'flowix' ), '.fx-sec--posts', $b ], 398 263 'post_card' => [ __( 'Post Cards', 'flowix' ), '.fx-post-card', $b ], 399 'ti_sec' => [ __( 'Image + Text Sections', 'flowix' ), '.fx-sec--text_image', $b ],400 'ti_img' => [ __( 'Image + Text — Image', 'flowix' ), '.fx-ti-img', [ 'image' ] ],401 'ti_text' => [ __( 'Image + Text — Text', 'flowix' ), '.fx-ti-text', $tb ],402 'features_sec' => [ __( 'Feature Icon Sections', 'flowix' ), '.fx-sec--features', $b ],403 'feature' => [ __( 'Feature Box', 'flowix' ), '.fx-feature', $b ],404 'feature_icon' => [ __( 'Feature Icon', 'flowix' ), '.fx-feature-icon', [ 'icon', 'box' ] ],405 'feature_title' => [ __( 'Feature Title', 'flowix' ), '.fx-feature-title', $tb ],406 'feature_text' => [ __( 'Feature Text', 'flowix' ), '.fx-feature-text', $tb ],407 'cta_sec' => [ __( 'Call-to-Action Sections', 'flowix' ), '.fx-sec--cta', $b ],408 'cta_text' => [ __( 'Call-to-Action Text', 'flowix' ), '.fx-cta-text', $tb ],409 264 ], 410 265 ]; … … 429 284 'type' => 'hidden', 430 285 'priority' => 1, 431 'description' => '<span class="notice notice-warning inline " style="display:block;padding:8px 12px;margin:0 0 12px">' . esc_html__( 'Your home page is set to show your latest posts, so these sections are not shown. To use them, choose "A static page" as your homepage.', 'flowix' ) . '<br><button type="button" class="button button-primary" style="margin-top:8px" data-flowix-static-home>' . esc_html__( 'Choose a home page', 'flowix' ) . '</button></span>',286 'description' => '<span class="notice notice-warning inline fx-cz-notice">' . esc_html__( 'Your home page is set to show your latest posts, so these sections are not shown. To use them, choose "A static page" as your homepage.', 'flowix' ) . '<br><button type="button" class="button button-primary" data-flowix-static-home>' . esc_html__( 'Choose a home page', 'flowix' ) . '</button></span>', 432 287 'active_callback' => function () { 433 288 return 'page' !== get_option( 'show_on_front' ); … … 455 310 foreach ( flowix_section_types() as $t ) { 456 311 $icons[ $t['icon'] ] = flowix_icon_library()[ $t['icon'] ] ?? ''; 457 }458 foreach ( array_keys( flowix_feature_icons() ) as $n ) {459 $icons[ $n ] = flowix_icon_library()[ $n ] ?? '';460 312 } 461 313 $defaults = []; -
flowix/1.1.2/inc/shop-features.php
r353588 r356478 3 3 * Flowix — Shop Features (WooCommerce boosters, no plugins needed) 4 4 * 5 * Quick view, wishlist, colour/image/label swatches, badge designer, free-delivery bar, 6 * sale countdown, low-stock urgency, delivery estimate, trust badges, Buy now, 7 * added-to-cart behaviour, product page layout, load more / infinite scroll, recently viewed. 5 * Shop display options: badges, free-delivery bar, trust badges, added-to-cart behaviour, 6 * product page layout and more. 8 7 * 9 8 * Settings live in Customize → Flowix — Layout Builder → Shop Features (theme_mods flowix_shop_*). … … 74 73 'text' => 'sanitize_text_field', 75 74 'textarea' => 'sanitize_textarea_field', 76 'select' => ' sanitize_key',75 'select' => 'flowix_sanitize_choice', 77 76 ][ $s[0] ]; 78 77 $wp_customize->add_setting( $id, [ 'default' => $s[2], 'sanitize_callback' => $sanitize, 'transport' => 'refresh' ] ); … … 166 165 add_action( 'woocommerce_before_single_product_summary', function() { 167 166 global $product; 168 echo flowix_ product_badges( $product ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped167 echo flowix_kses( flowix_product_badges( $product ) ); 169 168 }, 10 ); 170 169 171 /** Card extras on WooCommerce's shop-page cards: badges, quick view, wishlist, colour dots. */170 /** Extras on WooCommerce's shop-page cards (badges and card buttons). */ 172 171 add_action( 'woocommerce_before_shop_loop_item_title', function() { 173 172 global $product; 174 echo flowix_ card_overlay( $product ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped173 echo flowix_kses( flowix_card_overlay( $product ) ); 175 174 }, 9 ); 176 175 … … 186 185 187 186 188 /* Header builder item : wishlist link with count. */187 /* Header builder items added by the shop features. */ 189 188 add_filter( 'flowix_header_items_extra', function( $items ) { 190 189 $items['freeship'] = [ 'label' => __( 'Free-Delivery Progress', 'flowix' ), 'icon' => 'truck' ]; … … 209 208 $icon = count( $parts ) > 1 ? sanitize_key( $parts[0] ) : 'check'; 210 209 $text = end( $parts ); 211 echo '<li>' . flowix_inline_icon( isset( flowix_icon_library()[ $icon ] ) ? $icon : 'check', 18 ) . '<span>' . esc_html( $text ) . '</span></li>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped210 echo flowix_kses( '<li>' . flowix_inline_icon( isset( flowix_icon_library()[ $icon ] ) ? $icon : 'check', 18 ) . '<span>' . esc_html( $text ) . '</span></li>' ); 212 211 } 213 212 echo '</ul>'; … … 272 271 $pc = min( 100, round( $total / $goal * 100 ) ); 273 272 $msg = $left > 0 ? str_replace( '{amount}', wp_strip_all_tags( wc_price( $left ) ), flowix_shop( 'fsb_msg' ) ) : flowix_shop( 'fsb_done' ); 274 return '<div class="fx-fsb' . ( $left <= 0 ? ' is-done' : '' ) . ( $compact ? ' fx-fsb--compact' : '' ) . '"><p class="fx-fsb-msg">' . esc_html( $msg ) . '</p><span class="fx-fsb-track"><span class="fx-fsb-fill " style="width:' . esc_attr( $pc ) . '%"></span></span></div>';273 return '<div class="fx-fsb' . ( $left <= 0 ? ' is-done' : '' ) . ( $compact ? ' fx-fsb--compact' : '' ) . '"><p class="fx-fsb-msg">' . esc_html( $msg ) . '</p><span class="fx-fsb-track"><span class="fx-fsb-fill fx-fsb-fill--' . absint( round( $pc / 5 ) * 5 ) . '"></span></span></div>'; 275 274 } 276 275 277 276 add_action( 'woocommerce_before_cart_table', function() { 278 echo flowix_ free_shipping_bar(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped277 echo flowix_kses( flowix_free_shipping_bar() ); 279 278 } ); 280 279 add_action( 'woocommerce_before_mini_cart', function() { 281 echo flowix_ free_shipping_bar(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped280 echo flowix_kses( flowix_free_shipping_bar() ); 282 281 } ); 283 282 add_filter( 'woocommerce_add_to_cart_fragments', function( $f ) { -
flowix/1.1.2/index.php
r353748 r356478 7 7 8 8 get_header(); ?> 9 <div class="container fx-blog" style="padding-top:48px;padding-bottom:64px;">10 <header class="fx-blog-header " style="margin-bottom:36px;padding-bottom:24px;border-bottom:1px solid var(--border-light);">9 <div class="container fx-blog"> 10 <header class="fx-blog-header fx-page-header"> 11 11 <h1> 12 12 <?php -
flowix/1.1.2/page.php
r353588 r356478 8 8 $flowix_fx_notitle = $flowix_fx_canvas || get_post_meta( get_the_ID(), '_flowix_hide_title', true ); 9 9 ?> 10 <div class="container <?php echo $flowix_fx_canvas ? ' fx-canvas' : ''; ?>" style="<?php echo $flowix_fx_canvas ? 'max-width:none;padding:0;' : 'max-width:var(--content-width);padding-top:52px;padding-bottom:72px;'; ?>">10 <div class="container <?php echo $flowix_fx_canvas ? 'fx-canvas' : 'fx-narrow'; ?>"> 11 11 <?php while ( have_posts() ) : the_post(); ?> 12 12 <article <?php post_class(); ?>> 13 13 <?php if ( get_the_title() && ! $flowix_fx_notitle ) : ?> 14 <header style="margin-bottom:36px;padding-bottom:24px;border-bottom:1px solid var(--border-light);">14 <header class="fx-page-header"> 15 15 <h1><?php the_title(); ?></h1> 16 16 </header> … … 26 26 </div> 27 27 <?php if ( get_edit_post_link() && ! $flowix_fx_canvas ) : ?> 28 <footer style="margin-top:32px;">28 <footer class="fx-page-footer"> 29 29 <?php edit_post_link( __( 'Edit this page', 'flowix' ), '<span class="edit-link">', '</span>' ); ?> 30 30 </footer> -
flowix/1.1.2/readme.txt
r353748 r356478 1 1 === Flowix === 2 Contributors: djarnd3 Tags: e-commerce, custom-colors, custom-logo, custom-menu, editor-style, featured-images, footer-widgets, theme-options, threaded-comments, translation-ready, blog, wide-blocks2 Contributors: goodiez 3 Tags: e-commerce, custom-colors, custom-logo, custom-menu, editor-style, featured-images, footer-widgets, theme-options, threaded-comments, translation-ready, rtl-language-support, blog, wide-blocks 4 4 Requires at least: 6.4 5 5 Tested up to: 7.1 6 6 Requires PHP: 8.0 7 Stable tag: 1.1. 17 Stable tag: 1.1.2 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 21 21 * **Click to style** — switch it on in the preview and click anything to jump straight to its settings. 22 22 * **Header builder** — drag the logo, menu, search, cart, account, buttons, contact details and social icons into a top bar, main row and bottom row, separately for desktop and mobile. Sticky, hide-on-scroll and shrink options. 23 * **Footer builder** — choose a column layout and drop in menus, text, contact details, a newsletter form, payment icons and more.24 * **Home page builder** — reorder , hide and add sections: hero, trust bar, categories, featured products, testimonials, image banners, image + text, feature icons, call to actionand spacers.23 * **Footer builder** — choose a column layout and drop in menus, text, contact details, social icons, payment icons and more. 24 * **Home page builder** — reorder and hide the home page sections (hero, trust bar, page content, categories, featured products) and add latest-posts sections and spacers. 25 25 * **One-click looks** — start from Blank Canvas, Original Artisan or Swiss Grid. 26 26 * **Dark mode** — automatic, always on, or a switch your shoppers can use. 27 27 * **Shop extras** — sale badges with the discount percentage, "New" and "Sold out" badges, a free-delivery progress bar in the cart, trust badges under Add to cart, product page layout options, and an "added to cart" message or slide-out cart. 28 28 * **Page layout options** — hide the title or use a full-width canvas on any page. 29 * **Undo, redo & Before/After** — step back through your Customizer changes and compare with how your site looked before. 30 * **Right-to-left languages** — the layout mirrors for Arabic, Hebrew and other RTL languages. 29 31 * **Export, import & reset** your design. 30 31 Flowix Premium (sold separately at https://goodiez.co.za/flowix) adds more looks, backgrounds, motion and particle effects, content blocks and extra shop features. The free theme is complete on its own.32 32 33 33 == Installation == … … 52 52 Yes. Everything is stored in the Customizer settings, not in the theme files. 53 53 54 = Does Flowix collect any data? = 55 56 No. Flowix does not collect, store or send any personal data, and has no tracking. 57 58 * Dark mode switch: when a visitor uses the switch, their choice (light or dark) is remembered in their own browser (localStorage key "flowix-theme"). It is never sent to your site or anyone else. 59 * Fonts: the fonts you choose in the Brand Kit are loaded from Google Fonts (fonts.googleapis.com and fonts.gstatic.com), which means visitors' browsers connect to Google to download them. See Google's privacy policy: https://policies.google.com/privacy 60 54 61 == Changelog == 62 63 = 1.1.2 = 64 * New: undo and redo in the Customizer, a history of your changes, and a Before/After view to compare your design with how it was. 65 * New: right-to-left language support (Arabic, Hebrew and others). 66 * Fix: the header menu no longer overlaps a centred logo. 67 * WordPress.org review fixes: version numbers in sync, translation template (languages/flowix.pot) included, removed unused page template entries. 68 * Accessibility: mobile menu button has a name and announces open/closed; keyboard focus stays inside the search, cart and menu panels while they are open and returns when they close (Escape closes them); dropdown menus open with the keyboard; the skip link shows below the admin toolbar. 69 * Long titles and links now wrap instead of overflowing; floated images and embeds no longer push the sidebar down. 70 * Template layout styles moved from the templates into the stylesheet. 71 * Removed the newsletter form and the testimonial, vendor and free-text home page sections (WordPress.org content-creation rules). 72 * Posts split over several pages now show their page links. Clearer keyboard focus on form fields. 73 * Theme styles and the dark mode script now load through WordPress's own functions; removed schema markup and leftover inline styles. 74 * The footer tagline and copyright line are now set only in the Footer Builder (the duplicate settings under Flowix Theme → Footer are gone). 55 75 56 76 = 1.1.1 = -
flowix/1.1.2/search.php
r319286 r356478 5 5 */ 6 6 get_header(); ?> 7 <div class="container " style="padding-top:48px;padding-bottom:72px;">8 <header style="margin-bottom:36px;padding-bottom:24px;border-bottom:1px solid var(--border-light);">7 <div class="container fx-list-wrap"> 8 <header class="fx-page-header"> 9 9 <span class="eyebrow"><?php esc_html_e( 'Search Results', 'flowix' ); ?></span> 10 10 <h1> … … 13 13 /* translators: %s: search query */ 14 14 esc_html__( 'Results for: "%s"', 'flowix' ), 15 '<em>' . get_search_query() . '</em>'15 '<em>' . esc_html( get_search_query( false ) ) . '</em>' 16 16 ); 17 17 ?> 18 18 </h1> 19 19 <?php if ( have_posts() ) : ?> 20 <p class="lead " style="margin-top:8px;">20 <p class="lead fx-page-sub"> 21 21 <?php 22 22 printf( … … 31 31 32 32 <?php if ( have_posts() ) : ?> 33 <div class="products-grid " style="grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:24px;">33 <div class="products-grid fx-search-grid"> 34 34 <?php while ( have_posts() ) : the_post(); ?> 35 35 <article <?php post_class( 'product-card' ); ?>> 36 36 <?php if ( has_post_thumbnail() ) : ?> 37 37 <div class="product-card-media"> 38 <a href="<?php the_permalink(); ?>"><?php the_post_thumbnail( 'flowix- product-card' ); ?></a>38 <a href="<?php the_permalink(); ?>"><?php the_post_thumbnail( 'flowix-blog-thumb' ); ?></a> 39 39 </div> 40 40 <?php endif; ?> 41 41 <div class="product-card-body"> 42 <h2 class="product-card-title " style="font-size:1.05rem;">42 <h2 class="product-card-title fx-search-title"> 43 43 <a href="<?php the_permalink(); ?>"><?php the_title(); ?></a> 44 44 </h2> 45 < p style="font-size:0.88rem;color:var(--walnut);margin:0 0 14px;"><?php the_excerpt(); ?></p>45 <div class="fx-search-excerpt"><?php the_excerpt(); ?></div> 46 46 <a href="<?php the_permalink(); ?>" class="btn btn--sm"><?php esc_html_e( 'View', 'flowix' ); ?></a> 47 47 </div> … … 49 49 <?php endwhile; ?> 50 50 </div> 51 <div style="margin-top:40px;">51 <div class="fx-pagination-wrap"> 52 52 <?php the_posts_pagination(); ?> 53 53 </div> 54 54 <?php else : ?> 55 <div style="text-align:center;padding:60px 20px;">56 <h2 style="font-size:1.4rem;margin-bottom:16px;"><?php esc_html_e( 'Nothing found.', 'flowix' ); ?></h2>57 <p style="color:var(--walnut);margin-bottom:28px;">55 <div class="fx-nothing"> 56 <h2><?php esc_html_e( 'Nothing found.', 'flowix' ); ?></h2> 57 <p> 58 58 <?php esc_html_e( "We couldn't find anything matching your search. Try different keywords or browse our shop.", 'flowix' ); ?> 59 59 </p> -
flowix/1.1.2/single.php
r353588 r356478 5 5 */ 6 6 get_header(); ?> 7 <div class="container " style="max-width:var(--content-width);padding-top:52px;padding-bottom:72px;">7 <div class="container fx-narrow"> 8 8 <?php while ( have_posts() ) : the_post(); ?> 9 <article <?php post_class(); ?> itemscope itemtype="https://schema.org/BlogPosting">10 <header style="margin-bottom:32px;">11 <div style="display:flex;align-items:center;gap:16px;margin-bottom:14px;flex-wrap:wrap;">9 <article <?php post_class(); ?>> 10 <header class="fx-post-header"> 11 <div class="fx-post-meta"> 12 12 <?php 13 13 $flowix_cats = get_the_category(); 14 14 if ( $flowix_cats ) { 15 echo '<a href="' . esc_url( get_category_link( $flowix_cats[0]->term_id ) ) . '" class="eyebrow" style="margin:0;">' . esc_html( $flowix_cats[0]->name ) . '</a>';15 echo '<a href="' . esc_url( get_category_link( $flowix_cats[0]->term_id ) ) . '" class="eyebrow">' . esc_html( $flowix_cats[0]->name ) . '</a>'; 16 16 } 17 17 ?> 18 <time datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>" 19 style="font-family:var(--font-ui);font-size:0.65rem;letter-spacing:0.14em;text-transform:uppercase;color:var(--walnut);"> 18 <time class="fx-post-date" datetime="<?php echo esc_attr( get_the_date( 'c' ) ); ?>"> 20 19 <?php echo esc_html( get_the_date() ); ?> 21 20 </time> 22 21 </div> 23 <h1 itemprop="headline"><?php the_title(); ?></h1>22 <h1><?php the_title(); ?></h1> 24 23 </header> 25 24 26 25 <?php if ( has_post_thumbnail() ) : ?> 27 <div style="margin-bottom:36px;border-radius:var(--radius-sm);overflow:hidden;">28 <?php the_post_thumbnail( 'flowix-blog-thumb' , [ 'itemprop' => 'image', 'style' => 'width:100%;' ]); ?>26 <div class="fx-post-thumb"> 27 <?php the_post_thumbnail( 'flowix-blog-thumb' ); ?> 29 28 </div> 30 29 <?php endif; ?> 31 30 32 <div class="entry-content" itemprop="articleBody"> 33 <?php the_content(); ?> 31 <div class="entry-content"> 32 <?php 33 the_content(); 34 wp_link_pages( [ 35 'before' => '<div class="page-links"><span>' . esc_html__( 'Pages:', 'flowix' ) . '</span>', 36 'after' => '</div>', 37 ] ); 38 ?> 34 39 </div> 35 40 36 <footer style="margin-top:40px;padding-top:24px;border-top:1px solid var(--border-light);">37 <?php the_tags( '<div style="margin-bottom:16px;">', ', ', '</div>' ); ?>41 <footer class="fx-post-footer"> 42 <?php the_tags( '<div class="fx-post-tags">', ', ', '</div>' ); ?> 38 43 <?php 39 44 the_post_navigation( [ … … 46 51 <?php endwhile; ?> 47 52 <?php if ( comments_open() || get_comments_number() ) : ?> 48 <div class="container " style="max-width:var(--content-width);padding-bottom:72px;">53 <div class="container fx-narrow fx-comments-wrap"> 49 54 <?php comments_template(); ?> 50 55 </div> -
flowix/1.1.2/style.css
r353748 r356478 5 5 Author URI: https://goodiez.co.za 6 6 Description: A clean, fast WooCommerce theme you can restyle element by element from the Customizer, no code needed. Brand Kit for colours and fonts, per-element styling with "click to style", header, footer and home page builders, one-click looks, dark mode, and shop extras like discount badges, a free-delivery bar and trust badges. 7 Version: 1.1. 17 Version: 1.1.2 8 8 Requires at least: 6.4 9 9 Tested up to: 7.1 … … 12 12 License URI: http://www.gnu.org/licenses/gpl-2.0.html 13 13 Text Domain: flowix 14 Tags: e-commerce, custom-colors, custom-logo, custom-menu, editor-style, featured-images, footer-widgets, theme-options, threaded-comments, translation-ready, blog, wide-blocks14 Tags: e-commerce, custom-colors, custom-logo, custom-menu, editor-style, featured-images, footer-widgets, theme-options, threaded-comments, translation-ready, rtl-language-support, blog, wide-blocks 15 15 16 16 Flowix WordPress Theme, Copyright 2026 Djarn Dhevron … … 134 134 } 135 135 .skip-link:focus { top: 0; } 136 .admin-bar .skip-link:focus { top: 32px; } 137 @media (max-width: 782px) { .admin-bar .skip-link:focus { top: 46px; } } 136 138 137 139 img { max-width: 100%; height: auto; display: block; } … … 433 435 box-shadow: 0 0 0 3px rgba(155,123,58,0.12); 434 436 } 437 /* Keyboard focus: a clear ring on fields (links and buttons keep the browser's own ring). */ 438 input:focus-visible, 439 textarea:focus-visible, 440 select:focus-visible { 441 outline: 2px solid var(--gold); 442 outline-offset: 1px; 443 } 435 444 436 445 input::placeholder, … … 641 650 } 642 651 643 .primary-nav > li:hover .sub-menu { 652 .primary-nav > li:hover .sub-menu, 653 .primary-nav > li.is-open .sub-menu { 644 654 opacity: 1; 645 655 pointer-events: all; … … 2119 2129 .woocommerce-info { border-top-color: var(--caramel) !important; } 2120 2130 2121 /* ╔══════════════════════════════════════════════2122 ║ TESTIMONIALS SECTION2123 ╚══════════════════════════════════════════════ */2124 .testimonials-grid {2125 display: grid;2126 grid-template-columns: repeat(3, 1fr);2127 gap: 24px;2128 }2129 2130 .testimonial-card {2131 background: var(--bg-surface, #fff);2132 border: 1px solid var(--bd-light, var(--border-light));2133 border-radius: var(--radius-sm);2134 padding: 32px;2135 position: relative;2136 }2137 2138 .testimonial-card::before {2139 content: '\201C';2140 font-family: var(--font-accent);2141 font-size: 5rem;2142 color: var(--gold-pale);2143 position: absolute;2144 top: 16px;2145 left: 24px;2146 line-height: 1;2147 }2148 2149 .testimonial-quote {2150 font-family: var(--font-accent);2151 font-size: 1rem;2152 font-style: italic;2153 color: var(--tx-muted, var(--walnut));2154 line-height: 1.7;2155 margin-bottom: 24px;2156 position: relative;2157 z-index: 1;2158 padding-top: 20px;2159 }2160 2161 .testimonial-author {2162 display: flex;2163 align-items: center;2164 gap: 12px;2165 border-top: 1px solid var(--bd-light, var(--border-light));2166 padding-top: 20px;2167 }2168 2169 .testimonial-avatar {2170 width: 44px;2171 height: 44px;2172 border-radius: 50%;2173 object-fit: cover;2174 background: var(--bg-tint2, var(--linen));2175 }2176 2177 .testimonial-name {2178 font-family: var(--font-ui);2179 font-size: 0.72rem;2180 font-weight: 700;2181 letter-spacing: 0.1em;2182 text-transform: uppercase;2183 color: var(--tx-mid, var(--mahogany));2184 }2185 2186 .testimonial-meta {2187 font-family: var(--font-body);2188 font-size: 0.8rem;2189 color: var(--border);2190 }2191 2192 .testimonial-stars {2193 color: var(--amber);2194 font-size: 0.7rem;2195 letter-spacing: 2px;2196 margin-bottom: 4px;2197 }2198 2199 /* ╔══════════════════════════════════════════════2200 ║ VENDOR / SELLER CTA SECTION2201 ╚══════════════════════════════════════════════ */2202 .vendor-cta {2203 background: var(--espresso);2204 padding: 72px 0;2205 position: relative;2206 overflow: hidden;2207 }2208 2209 .vendor-cta::before {2210 content: '';2211 position: absolute;2212 top: -50%;2213 right: -10%;2214 width: 500px;2215 height: 500px;2216 border-radius: 50%;2217 border: 1px solid rgba(155,123,58,0.1);2218 pointer-events: none;2219 }2220 2221 .vendor-cta::after {2222 content: '';2223 position: absolute;2224 bottom: -60%;2225 right: 5%;2226 width: 300px;2227 height: 300px;2228 border-radius: 50%;2229 border: 1px solid rgba(155,123,58,0.07);2230 pointer-events: none;2231 }2232 2233 .vendor-cta-inner {2234 position: relative;2235 z-index: 1;2236 display: grid;2237 grid-template-columns: 1fr auto;2238 gap: 48px;2239 align-items: center;2240 }2241 2242 .vendor-cta h2 {2243 font-family: var(--font-display);2244 font-size: clamp(1.6rem, 3vw, 2.4rem);2245 color: var(--cream);2246 margin-bottom: 12px;2247 font-weight: 400;2248 }2249 2250 .vendor-cta p {2251 color: rgba(237,228,212,0.7);2252 max-width: 480px;2253 font-size: 1rem;2254 margin-bottom: 0;2255 }2256 2257 .vendor-cta-actions {2258 display: flex;2259 gap: 14px;2260 flex-wrap: wrap;2261 justify-content: flex-end;2262 flex-shrink: 0;2263 }2264 2265 /* ╔══════════════════════════════════════════════2266 ║ NEWSLETTER SECTION2267 ╚══════════════════════════════════════════════ */2268 .newsletter-section {2269 background: var(--parchment);2270 border-top: 1px solid var(--border);2271 border-bottom: 1px solid var(--border);2272 padding: 48px 0;2273 text-align: center;2274 }2275 2276 .newsletter-form {2277 max-width: 480px;2278 margin: 24px auto 0;2279 display: flex;2280 gap: 0;2281 }2282 2283 .newsletter-form input[type="email"] {2284 border-radius: var(--radius-sm) 0 0 var(--radius-sm);2285 border-right: none;2286 background: #fff;2287 }2288 2289 .newsletter-form .btn {2290 border-radius: 0 var(--radius-sm) var(--radius-sm) 0;2291 white-space: nowrap;2292 padding: 13px 28px;2293 font-size: 0.68rem;2294 background: var(--hr-tx, var(--espresso));2295 color: var(--hr-bg, var(--cream)) !important;2296 border-color: var(--hr-tx, var(--espresso));2297 flex-shrink: 0;2298 }2299 .newsletter-section { color: color-mix(in srgb, var(--hr-tx) 75%, transparent); }2300 .newsletter-section :is(h2, .lead) { color: var(--hr-tx); }2301 .newsletter-section .lead { opacity: .75; }2302 .newsletter-section .eyebrow { color: var(--hr-acc); }2303 2131 2304 2132 /* ╔══════════════════════════════════════════════ … … 2629 2457 .header-topbar { display: none; } 2630 2458 .shop-layout { grid-template-columns: 1fr; } 2631 .testimonials-grid { grid-template-columns: 1fr; }2632 .vendor-cta-inner { grid-template-columns: 1fr; }2633 .vendor-cta-actions { justify-content: flex-start; }2634 2459 .single-product-layout { grid-template-columns: 1fr; gap: 32px; } 2635 2460 } … … 2643 2468 .trust-bar-items { flex-direction: column; gap: 8px; align-items: flex-start; } 2644 2469 .trust-item { border-right: none; padding: 6px 0; } 2645 .newsletter-form { flex-direction: column; }2646 .newsletter-form input[type="email"] { border-radius: var(--radius-sm); border-right: 1.5px solid var(--border); }2647 .newsletter-form .btn { border-radius: var(--radius-sm); width: 100%; }2648 2470 .woocommerce-cart .cart-collaterals .cart_totals { float: none; width: 100%; } 2649 2471 .woocommerce-MyAccount-navigation { width: 100%; } … … 2793 2615 .fx-blog .page-numbers.current { background: var(--ink, #111); color: #fff; border-color: transparent; } 2794 2616 .fx-page-content .entry-content > *:first-child { margin-top: 0; } 2617 2618 /* ╔══════════════════════════════════════════════ 2619 ║ TEMPLATE LAYOUT (pages, posts, search, 404) 2620 ╚══════════════════════════════════════════════ */ 2621 .fx-narrow { max-width: var(--content-width); padding-top: 52px; padding-bottom: 72px; } 2622 .fx-canvas { max-width: none; padding: 0; } 2623 .fx-comments-wrap { padding-top: 0; } 2624 .fx-list-wrap { padding-top: 48px; padding-bottom: 72px; } 2625 .fx-blog { padding-top: 48px; padding-bottom: 64px; } 2626 .fx-page-header { margin-bottom: 36px; padding-bottom: 24px; border-bottom: 1px solid var(--border-light); } 2627 .fx-page-sub { margin-top: 8px; } 2628 .fx-page-footer { margin-top: 32px; } 2629 .fx-post-header { margin-bottom: 32px; } 2630 .fx-post-meta { display: flex; align-items: center; gap: 16px; margin-bottom: 14px; flex-wrap: wrap; } 2631 .fx-post-meta .eyebrow { margin: 0; } 2632 .fx-post-date { font-family: var(--font-ui); font-size: 0.65rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--walnut); } 2633 .fx-post-thumb { margin-bottom: 36px; border-radius: var(--radius-sm); overflow: hidden; } 2634 .fx-post-thumb img { width: 100%; } 2635 .fx-post-footer { margin-top: 40px; padding-top: 24px; border-top: 1px solid var(--border-light); } 2636 .fx-post-tags { margin-bottom: 16px; } 2637 .fx-search-grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 24px; } 2638 .fx-search-title { font-size: 1.05rem; } 2639 .fx-search-excerpt { font-size: 0.88rem; color: var(--walnut); margin: 0 0 14px; } 2640 .fx-search-excerpt p { margin: 0; } 2641 .fx-pagination-wrap { margin-top: 40px; } 2642 .fx-nothing { text-align: center; padding: 60px 20px; } 2643 .fx-nothing h2 { font-size: 1.4rem; margin-bottom: 16px; } 2644 .fx-nothing p { color: var(--walnut); margin-bottom: 28px; } 2645 .error-404 h1 { margin-bottom: 16px; } 2646 .error-404 .lead { max-width: 440px; margin: 0 auto 36px; } 2647 .error-404-actions { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; } 2648 .mobile-nav-header .site-title { color: var(--cream); font-size: 1.2rem; } 2649 .mobile-nav-footer { padding: 20px 24px; border-top: 1px solid rgba(155, 123, 58, 0.2); margin-top: auto; } 2650 .btn--block { display: block; width: 100%; text-align: center; } 2651 .btn--ghost-light { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.2); color: #fff; } 2652 .fx-section-more { text-align: center; margin-top: var(--space-xl); } 2653 2654 /* ╔══════════════════════════════════════════════ 2655 ║ LONG WORDS, FLOATS AND KEYBOARD PANELS 2656 ╚══════════════════════════════════════════════ */ 2657 body { overflow-wrap: break-word; } 2658 h1, h2, h3, h4, h5, h6, .entry-title, .product_title, .widget-title { overflow-wrap: break-word; word-break: break-word; hyphens: auto; } 2659 .entry-content a, .comment-content a, .widget a { overflow-wrap: anywhere; } 2660 .entry-content::after, .comment-content::after, .widget::after, .clearfix::after { content: ""; display: table; clear: both; } 2661 .clear { clear: both; } 2662 .mini-cart-drawer:not(.open), .mobile-nav-drawer:not(.open), .search-overlay:not(.open) { visibility: hidden; transition-property: transform, opacity, visibility; transition-delay: 0s, 0s, var(--dur-lg, .35s); } 2663 .mini-cart-drawer.open, .mobile-nav-drawer.open, .search-overlay.open { visibility: visible; transition-delay: 0s; } -
flowix/1.1.2/template-parts/home/featured.php
r353588 r356478 27 27 wp_reset_postdata(); ?> 28 28 </div> 29 <div style="text-align:center;margin-top:var(--space-xl);">29 <div class="fx-section-more"> 30 30 <?php if ( class_exists( 'WooCommerce' ) ) : ?> 31 31 <a href="<?php echo esc_url( get_permalink( wc_get_page_id( 'shop' ) ) ); ?>" class="btn btn--primary btn--lg"> -
flowix/1.1.2/template-parts/home/page_content.php
r353748 r356478 13 13 return; 14 14 } 15 $flowix_ page = get_post( $flowix_front);16 if ( ! $flowix_ page || '' === trim( (string) $flowix_page->post_content ) ) {15 $flowix_query = new WP_Query( [ 'page_id' => $flowix_front, 'post_type' => 'page' ] ); 16 if ( ! $flowix_query->have_posts() || '' === trim( (string) $flowix_query->posts[0]->post_content ) ) { 17 17 return; 18 18 } 19 $GLOBALS['post'] = $flowix_page; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited20 setup_postdata( $flowix_page );21 19 ?> 22 20 <section class="section fx-page-content"> 23 21 <div class="container entry-content"> 24 <?php the_content(); ?> 22 <?php 23 while ( $flowix_query->have_posts() ) { 24 $flowix_query->the_post(); 25 the_content(); 26 } 27 wp_reset_postdata(); 28 ?> 25 29 </div> 26 30 </section> 27 <?php28 wp_reset_postdata(); -
flowix/1.1.2/template-parts/home/trust.php
r353588 r356478 32 32 <div class="trust-item"> 33 33 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-hidden="true"> 34 <?php echo $flowix_svgs[ $flowix_i - 1 ]; // phpcs:ignore?>34 <?php echo flowix_kses( $flowix_svgs[ $flowix_i - 1 ] ); ?> 35 35 </svg> 36 36 <?php echo esc_html( $flowix_text ); ?> -
flowix/1.1.2/woocommerce.php
r353588 r356478 12 12 $flowix_wrapper_class = 'woocommerce-page-wrapper'; 13 13 if ( $flowix_has_sidebar ) $flowix_wrapper_class .= ' has-sidebar'; 14 if ( function_exists( 'flowix_cat_banner_on' ) && flowix_cat_banner_on() ) {15 echo flowix_cat_banner(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- escaped while built.16 }17 14 ?> 18 15
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)