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:
@@ -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>
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
<v-app>
|
||||||
|
<AppShell>
|
||||||
|
<RouterView />
|
||||||
|
</AppShell>
|
||||||
|
</v-app>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import AppShell from './components/AppShell.vue'
|
||||||
|
</script>
|
||||||
@@ -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')
|
||||||
@@ -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'
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user