Skip to content

Commit a8adc6b

Browse files
authored
Make /wordart responsive and fix atlas background warning (#52)
* fix(website): make /wordart responsive with gallery-style mobile tabs * fix(react): stop mixing background shorthand and longhands on atlas leaves
1 parent 2db3178 commit a8adc6b

3 files changed

Lines changed: 121 additions & 14 deletions

File tree

packages/react/src/scene/atlas/atlasPoly.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,18 @@ export const TextureAtlasPoly = memo(function TextureAtlasPoly({
5050
const style: CSSProperties = {
5151
transform: formatMatrix3d(entry.atlasMatrix),
5252
["--polycss-atlas-size" as string]: `${atlasCanonicalSize}px`,
53-
background,
54-
backgroundImage: dynamic && page?.url ? `url(${page.url})` : undefined,
55-
backgroundPosition: dynamic ? atlasPosition : undefined,
56-
backgroundSize: dynamic ? atlasSize : undefined,
53+
// Listing the `background` shorthand alongside the `background-*` longhands
54+
// in one inline style object makes React warn on every update (mixing
55+
// shorthand and non-shorthand for the same property). Branch so only the
56+
// current mode's keys are assigned — baked gets `background`, dynamic gets
57+
// the longhands.
58+
...(dynamic
59+
? {
60+
backgroundImage: page?.url ? `url(${page.url})` : undefined,
61+
backgroundPosition: atlasPosition,
62+
backgroundSize: atlasSize,
63+
}
64+
: { background }),
5765
...(dynamic
5866
? {
5967
["--pnx" as string]: entry.normal[0].toFixed(4),

website/src/components/WordArtWorkbench/WordArtWorkbench.tsx

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ export function WordArtWorkbench() {
157157
const [lightEl, setLightEl] = useState(() => qn("lel", 45));
158158
const [activePreset, setActivePreset] = useState<string | null>(null);
159159
const [exporting, setExporting] = useState(false);
160+
// Mobile: only one floating panel is open at a time, toggled by the bottom tabs.
161+
const [mobilePanel, setMobilePanel] = useState<"style" | "controls" | null>(null);
160162

161163
const handleCodePen = async () => {
162164
const el = document.querySelector<HTMLElement>(".wa-stage .polycss-camera")
@@ -173,6 +175,13 @@ export function WordArtWorkbench() {
173175
}
174176
};
175177

178+
useEffect(() => {
179+
if (!mobilePanel) return;
180+
const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") setMobilePanel(null); };
181+
window.addEventListener("keydown", onKey);
182+
return () => window.removeEventListener("keydown", onKey);
183+
}, [mobilePanel]);
184+
176185
// Default bundled font + Google catalog. If the URL named a font, select it
177186
// once the catalog is in.
178187
useEffect(() => {
@@ -366,7 +375,11 @@ export function WordArtWorkbench() {
366375
status={status}
367376
/>
368377

369-
<aside className="wa-card wa-left" aria-label="Text and presets">
378+
<aside
379+
id="wa-style-panel"
380+
className={`wa-card wa-left ${mobilePanel === "style" ? "is-mobile-open" : ""}`}
381+
aria-label="Text and presets"
382+
>
370383
<div className="wa-card__body">
371384
<label className="wa-field">
372385
<span>Text</span>
@@ -398,11 +411,37 @@ export function WordArtWorkbench() {
398411
</div>
399412
</aside>
400413

401-
<GuiPanel values={guiValues} set={guiSet} />
414+
<GuiPanel
415+
id="wa-controls-panel"
416+
className={mobilePanel === "controls" ? "is-mobile-open" : ""}
417+
values={guiValues}
418+
set={guiSet}
419+
/>
402420

403421
<button type="button" className="wa-codepen" onClick={handleCodePen} disabled={exporting}>
404422
{exporting ? "Exporting…" : "Open in CodePen"}
405423
</button>
424+
425+
<nav className="wa-mobile-tabs" aria-label="WordArt panels">
426+
<button
427+
type="button"
428+
className={`wa-mobile-tabs__button ${mobilePanel === "style" ? "is-active" : ""}`}
429+
aria-controls="wa-style-panel"
430+
aria-expanded={mobilePanel === "style"}
431+
onClick={() => setMobilePanel((cur) => (cur === "style" ? null : "style"))}
432+
>
433+
Style
434+
</button>
435+
<button
436+
type="button"
437+
className={`wa-mobile-tabs__button ${mobilePanel === "controls" ? "is-active" : ""}`}
438+
aria-controls="wa-controls-panel"
439+
aria-expanded={mobilePanel === "controls"}
440+
onClick={() => setMobilePanel((cur) => (cur === "controls" ? null : "controls"))}
441+
>
442+
Controls
443+
</button>
444+
</nav>
406445
</div>
407446
);
408447
}
@@ -540,7 +579,7 @@ interface GuiValues {
540579
* are identical, not a CSS approximation. lil-gui is imperative, so we mount it
541580
* once and bridge its onChange → React, and React state → updateDisplay().
542581
*/
543-
function GuiPanel({ values, set }: { values: GuiValues; set: (k: keyof GuiValues, v: number | string | boolean) => void }) {
582+
function GuiPanel({ id, className = "", values, set }: { id?: string; className?: string; values: GuiValues; set: (k: keyof GuiValues, v: number | string | boolean) => void }) {
544583
const hostRef = useRef<HTMLDivElement>(null);
545584
const cfgRef = useRef<GuiValues>({ ...values });
546585
const ctrlRef = useRef<Record<string, ReturnType<GUI["add"]>>>({});
@@ -592,7 +631,7 @@ function GuiPanel({ values, set }: { values: GuiValues; set: (k: keyof GuiValues
592631
ctrlRef.current.profileSegments?.[values.profile === "round" ? "show" : "hide"]();
593632
});
594633

595-
return <div className="wa-gui" ref={hostRef} />;
634+
return <div id={id} className={`wa-gui ${className}`} ref={hostRef} />;
596635
}
597636

598637
interface LeftValues {

website/src/components/WordArtWorkbench/wordart.css

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,12 +122,6 @@
122122
}
123123
textarea.wa-input { resize: vertical; line-height: 1.35; height: auto; padding: 0.4rem 0.5rem; }
124124

125-
@media (max-width: 60rem) {
126-
.wa-card { position: static; width: auto; max-height: none; margin: 12px; }
127-
.wa-root { height: auto; overflow: visible; }
128-
.wa-stage { position: relative; height: 56vh; inset: auto; }
129-
}
130-
131125
/* Right panel = real lil-gui, themed with the /gallery's exact variables. */
132126
.wa-gui {
133127
position: absolute;
@@ -235,3 +229,69 @@ textarea.wa-input { resize: vertical; line-height: 1.35; height: auto; padding:
235229
}
236230
.wa-codepen:hover { border-color: #3a86ff; }
237231
.wa-codepen:disabled { opacity: 0.6; cursor: progress; }
232+
233+
/* Bottom tab bar — desktop keeps both panels floating, so it's hidden there. */
234+
.wa-mobile-tabs { display: none; }
235+
236+
/* Mobile: like /gallery — the floating panels collapse into bottom sheets that
237+
the tab bar toggles one at a time, over a full-bleed stage. */
238+
@media (max-width: 760px) {
239+
.wa-root { --wa-tabs-h: 52px; }
240+
241+
.dn-stats-overlay { display: none !important; }
242+
243+
/* Only the two top-level floating panels collapse — not the left card's own
244+
inline lil-gui (.wa-gui--inline), which stays inside the scrolling card. */
245+
.wa-left,
246+
.wa-gui:not(.wa-gui--inline) {
247+
top: 8px;
248+
right: 8px;
249+
bottom: calc(8px + var(--wa-tabs-h) + 8px);
250+
left: 8px;
251+
width: auto;
252+
max-width: none;
253+
max-height: none;
254+
z-index: 25;
255+
display: none;
256+
}
257+
.wa-left.is-mobile-open { display: flex; }
258+
.wa-gui:not(.wa-gui--inline).is-mobile-open { display: flex; flex-direction: column; }
259+
.wa-left .wa-card__body { flex: 1; max-height: none; }
260+
261+
/* The right panel's lil-gui fills the sheet and scrolls inside it. */
262+
.wa-gui:not(.wa-gui--inline) .lil-gui.root {
263+
width: 100% !important;
264+
height: 100% !important;
265+
max-height: 100% !important;
266+
}
267+
268+
.wa-codepen,
269+
.wa-stage-foot { bottom: calc(8px + var(--wa-tabs-h) + 8px); }
270+
271+
.wa-mobile-tabs {
272+
position: absolute;
273+
right: 8px;
274+
bottom: 8px;
275+
left: 8px;
276+
z-index: 30;
277+
display: grid;
278+
grid-template-columns: repeat(2, minmax(0, 1fr));
279+
gap: 8px;
280+
}
281+
.wa-mobile-tabs__button {
282+
min-height: 44px;
283+
border: 1px solid #2a3340;
284+
border-radius: 8px;
285+
background: rgba(17, 20, 26, 0.92);
286+
color: #d8e2ec;
287+
font: 700 13px/1 system-ui, sans-serif;
288+
cursor: pointer;
289+
backdrop-filter: blur(8px);
290+
-webkit-backdrop-filter: blur(8px);
291+
}
292+
.wa-mobile-tabs__button.is-active {
293+
border-color: #22d3ee;
294+
background: #12313a;
295+
color: #ecfeff;
296+
}
297+
}

0 commit comments

Comments
 (0)