feat: scaffold Vue 3 + Vuetify + Pinia frontend themed to FabledDesignSystem

Curator signature accent (#A87338, aged amber) is baked into
fabled-tokens.js. Vuetify theme consumes the same tokens module the rest
of the SPA does, so updating design system tokens propagates throughout.
Vite dev server proxies /api and /ws to the Quart backend.

App.vue references AppShell (lands in Task 9) and main.js references
router.js (lands in Task 9) — these are intentionally split so Task 8 is
the framework scaffold and Task 9 is the navigation/shell content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 07:35:33 -04:00
parent 8b5711abef
commit ad2ac31f1a
7 changed files with 175 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>FabledCurator</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Fraunces:wght@400;500;600&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
+26
View File
@@ -0,0 +1,26 @@
{
"name": "fabledcurator-frontend",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"check": "vue-tsc --noEmit"
},
"dependencies": {
"vue": "^3.4.0",
"vue-router": "^4.3.0",
"pinia": "^2.1.0",
"vuetify": "^3.5.0",
"@mdi/font": "^7.4.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
"vite": "^5.2.0",
"vue-tsc": "^2.0.0",
"vite-plugin-vuetify": "^2.0.0",
"sass": "^1.71.0"
}
}
+11
View File
@@ -0,0 +1,11 @@
<template>
<v-app>
<AppShell>
<RouterView />
</AppShell>
</v-app>
</template>
<script setup>
import AppShell from './components/AppShell.vue'
</script>
+26
View File
@@ -0,0 +1,26 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createVuetify } from 'vuetify'
import 'vuetify/styles'
import '@mdi/font/css/materialdesignicons.css'
import App from './App.vue'
import router from './router.js'
import { fabledCuratorTheme } from './theme/vuetify-theme.js'
const vuetify = createVuetify({
theme: {
defaultTheme: 'fabledCurator',
themes: { fabledCurator: fabledCuratorTheme }
},
defaults: {
// FabledDesignSystem: pill shape for buttons
VBtn: { rounded: 'pill' }
}
})
createApp(App)
.use(createPinia())
.use(router)
.use(vuetify)
.mount('#app')
+41
View File
@@ -0,0 +1,41 @@
// FabledDesignSystem tokens — mirrored from FabledDesignSystem-new/design-system.md.
// Do not hand-edit hexes; update the source-of-truth doc and re-mirror here.
export const surfaces = {
obsidian: '#14171A', // page background
iron: '#1E2228', // cards
slate: '#2C313A', // hovered surfaces
pewter: '#3F4651' // borders, ghost outlines
}
export const text = {
parchment: '#E8E4D8', // primary on dark
vellum: '#C2BFB4', // secondary
ash: '#9C9A92' // tertiary
}
export const action = {
moss: '#4A5D3F', // primary action
bronze: '#8B7355', // secondary action
pewter: '#3F4651', // tertiary/ghost
destructive: '#6B2118' // delete/irreversible
}
export const semantic = {
success: '#4A5D3F',
warning: '#8B6F1E',
error: '#C04A1F',
info: '#3D5A6E'
}
// Per-app signature accent. Curator's accent was added to FabledDesignSystem
// during FC-1 (2026-05-14). Aged amber — manuscript ink, curated archive.
export const accent = {
curator: '#A87338'
}
export const typography = {
display: 'Fraunces, Georgia, serif',
body: 'Inter, system-ui, sans-serif',
mono: '"JetBrains Mono", monospace'
}
+29
View File
@@ -0,0 +1,29 @@
import { surfaces, text, action, semantic, accent } from './fabled-tokens.js'
export const fabledCuratorTheme = {
dark: true,
colors: {
background: surfaces.obsidian,
surface: surfaces.iron,
'surface-bright': surfaces.slate,
'surface-light': surfaces.pewter,
primary: action.moss,
secondary: action.bronze,
tertiary: action.pewter,
'on-background': text.parchment,
'on-surface': text.parchment,
'on-primary': text.parchment,
'on-secondary': text.parchment,
success: semantic.success,
warning: semantic.warning,
error: semantic.error,
info: semantic.info,
// Curator signature accent — used for nav-active state, wordmark, selection.
// Not on action buttons (see design-system.md "Accent usage rules").
accent: accent.curator
}
}
+23
View File
@@ -0,0 +1,23 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vuetify from 'vite-plugin-vuetify'
export default defineConfig({
plugins: [
vue(),
vuetify({ autoImport: true })
],
build: {
outDir: 'dist',
emptyOutDir: true,
sourcemap: false
},
server: {
port: 5173,
proxy: {
// Dev server proxies API to local Quart
'/api': 'http://localhost:8080',
'/ws': { target: 'ws://localhost:8080', ws: true }
}
}
})