From b145bdabd38f319694e4ed1f36e190c94ed665bb Mon Sep 17 00:00:00 2001 From: Fabian Meyer <44942030+dinooo13@users.noreply.github.com> Date: Tue, 29 Sep 2026 15:59:18 +0200 Subject: [PATCH 01/16] Add design switcher infrastructure for redesign explorations Co-Authored-By: Claude Opus 5.5 (1M context) --- app/app.vue | 4 ++ app/assets/css/designs/editorial.css | 1 + app/assets/css/designs/signal.css | 1 + app/assets/css/designs/studio.css | 1 + app/assets/css/main.css | 8 ++++ app/components/DesignSwitcher.vue | 32 ++++++++++++++++ app/components/designs/editorial/Home.vue | 15 ++++++++ app/components/designs/editorial/Labs.vue | 14 +++++++ app/components/designs/editorial/Shell.vue | 9 +++++ app/components/designs/editorial/Speaking.vue | 14 +++++++ app/components/designs/signal/Home.vue | 15 ++++++++ app/components/designs/signal/Labs.vue | 14 +++++++ app/components/designs/signal/Shell.vue | 9 +++++ app/components/designs/signal/Speaking.vue | 14 +++++++ app/components/designs/studio/Home.vue | 15 ++++++++ app/components/designs/studio/Labs.vue | 14 +++++++ app/components/designs/studio/Shell.vue | 9 +++++ app/components/designs/studio/Speaking.vue | 14 +++++++ app/composables/useDesign.ts | 37 +++++++++++++++++++ app/layouts/default.vue | 12 +++++- app/pages/index.vue | 33 ++++++++++++++--- app/pages/labs/index.vue | 19 +++++++++- app/pages/speaking/index.vue | 19 +++++++++- app/plugins/design.client.ts | 23 ++++++++++++ app/utils/designs.ts | 19 ++++++++++ 25 files changed, 357 insertions(+), 8 deletions(-) create mode 100644 app/assets/css/designs/editorial.css create mode 100644 app/assets/css/designs/signal.css create mode 100644 app/assets/css/designs/studio.css create mode 100644 app/components/DesignSwitcher.vue create mode 100644 app/components/designs/editorial/Home.vue create mode 100644 app/components/designs/editorial/Labs.vue create mode 100644 app/components/designs/editorial/Shell.vue create mode 100644 app/components/designs/editorial/Speaking.vue create mode 100644 app/components/designs/signal/Home.vue create mode 100644 app/components/designs/signal/Labs.vue create mode 100644 app/components/designs/signal/Shell.vue create mode 100644 app/components/designs/signal/Speaking.vue create mode 100644 app/components/designs/studio/Home.vue create mode 100644 app/components/designs/studio/Labs.vue create mode 100644 app/components/designs/studio/Shell.vue create mode 100644 app/components/designs/studio/Speaking.vue create mode 100644 app/composables/useDesign.ts create mode 100644 app/plugins/design.client.ts create mode 100644 app/utils/designs.ts diff --git a/app/app.vue b/app/app.vue index 4bda9a4..73f457b 100644 --- a/app/app.vue +++ b/app/app.vue @@ -29,6 +29,9 @@ useHead({ { rel: 'me', href: 'https://github.com/dinooo13' }, { rel: 'me', href: 'https://linkedin.com/in/fabian-meyer-02038813a' } ], + script: [ + { key: 'design-boot', innerHTML: designBootScript, tagPosition: 'head' } + ], htmlAttrs: { lang: 'en' } @@ -70,5 +73,6 @@ useSchemaOrg([ + diff --git a/app/assets/css/designs/editorial.css b/app/assets/css/designs/editorial.css new file mode 100644 index 0000000..6f136da --- /dev/null +++ b/app/assets/css/designs/editorial.css @@ -0,0 +1 @@ +/* editorial design — every rule must be scoped under [data-design="editorial"] */ diff --git a/app/assets/css/designs/signal.css b/app/assets/css/designs/signal.css new file mode 100644 index 0000000..4203131 --- /dev/null +++ b/app/assets/css/designs/signal.css @@ -0,0 +1 @@ +/* signal design — every rule must be scoped under [data-design="signal"] */ diff --git a/app/assets/css/designs/studio.css b/app/assets/css/designs/studio.css new file mode 100644 index 0000000..e87264b --- /dev/null +++ b/app/assets/css/designs/studio.css @@ -0,0 +1 @@ +/* studio design — every rule must be scoped under [data-design="studio"] */ diff --git a/app/assets/css/main.css b/app/assets/css/main.css index 271072b..bb69f04 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -1,5 +1,8 @@ @import "tailwindcss"; @import "@nuxt/ui"; +@import "./designs/editorial.css"; +@import "./designs/signal.css"; +@import "./designs/studio.css"; @source "../../../content/**/*"; @@ -30,6 +33,11 @@ border-radius: 0.25rem; } +/* Hide the prerendered classic markup until a non-default design has swapped in. */ +html.design-pending #__nuxt { + visibility: hidden; +} + [id="main-content"]:focus { outline: none; } diff --git a/app/components/DesignSwitcher.vue b/app/components/DesignSwitcher.vue new file mode 100644 index 0000000..e33a12e --- /dev/null +++ b/app/components/DesignSwitcher.vue @@ -0,0 +1,32 @@ + + + diff --git a/app/components/designs/editorial/Home.vue b/app/components/designs/editorial/Home.vue new file mode 100644 index 0000000..470f6c0 --- /dev/null +++ b/app/components/designs/editorial/Home.vue @@ -0,0 +1,15 @@ + + + diff --git a/app/components/designs/editorial/Labs.vue b/app/components/designs/editorial/Labs.vue new file mode 100644 index 0000000..f7c813a --- /dev/null +++ b/app/components/designs/editorial/Labs.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/designs/editorial/Shell.vue b/app/components/designs/editorial/Shell.vue new file mode 100644 index 0000000..3350c60 --- /dev/null +++ b/app/components/designs/editorial/Shell.vue @@ -0,0 +1,9 @@ + diff --git a/app/components/designs/editorial/Speaking.vue b/app/components/designs/editorial/Speaking.vue new file mode 100644 index 0000000..e8e688e --- /dev/null +++ b/app/components/designs/editorial/Speaking.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/designs/signal/Home.vue b/app/components/designs/signal/Home.vue new file mode 100644 index 0000000..470f6c0 --- /dev/null +++ b/app/components/designs/signal/Home.vue @@ -0,0 +1,15 @@ + + + diff --git a/app/components/designs/signal/Labs.vue b/app/components/designs/signal/Labs.vue new file mode 100644 index 0000000..f7c813a --- /dev/null +++ b/app/components/designs/signal/Labs.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/designs/signal/Shell.vue b/app/components/designs/signal/Shell.vue new file mode 100644 index 0000000..3350c60 --- /dev/null +++ b/app/components/designs/signal/Shell.vue @@ -0,0 +1,9 @@ + diff --git a/app/components/designs/signal/Speaking.vue b/app/components/designs/signal/Speaking.vue new file mode 100644 index 0000000..e8e688e --- /dev/null +++ b/app/components/designs/signal/Speaking.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/designs/studio/Home.vue b/app/components/designs/studio/Home.vue new file mode 100644 index 0000000..470f6c0 --- /dev/null +++ b/app/components/designs/studio/Home.vue @@ -0,0 +1,15 @@ + + + diff --git a/app/components/designs/studio/Labs.vue b/app/components/designs/studio/Labs.vue new file mode 100644 index 0000000..f7c813a --- /dev/null +++ b/app/components/designs/studio/Labs.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/components/designs/studio/Shell.vue b/app/components/designs/studio/Shell.vue new file mode 100644 index 0000000..3350c60 --- /dev/null +++ b/app/components/designs/studio/Shell.vue @@ -0,0 +1,9 @@ + diff --git a/app/components/designs/studio/Speaking.vue b/app/components/designs/studio/Speaking.vue new file mode 100644 index 0000000..e8e688e --- /dev/null +++ b/app/components/designs/studio/Speaking.vue @@ -0,0 +1,14 @@ + + + diff --git a/app/composables/useDesign.ts b/app/composables/useDesign.ts new file mode 100644 index 0000000..4417f8f --- /dev/null +++ b/app/composables/useDesign.ts @@ -0,0 +1,37 @@ +export const useDesign = () => { + const design = useState('design', () => 'classic') + + const applyDesign = (id: DesignId) => { + design.value = id + + if (import.meta.client) { + document.documentElement.setAttribute('data-design', id) + + try { + localStorage.setItem(DESIGN_STORAGE_KEY, id) + } catch { + // Storage can be unavailable (private mode); the switch still works for this visit. + } + } + } + + const setDesign = (id: DesignId) => { + if (id === design.value) return + + if (import.meta.client && document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) { + document.startViewTransition(async () => { + applyDesign(id) + await nextTick() + }) + return + } + + applyDesign(id) + } + + return { + design: readonly(design), + setDesign, + applyDesign + } +} diff --git a/app/layouts/default.vue b/app/layouts/default.vue index 408f8e9..dc4fdfb 100644 --- a/app/layouts/default.vue +++ b/app/layouts/default.vue @@ -1,8 +1,18 @@