From cd737af4abada93a9b58939f1ef1e05120ad8bba Mon Sep 17 00:00:00 2001 From: Matei Date: Mon, 8 Jun 2026 23:34:36 -0400 Subject: [PATCH 1/3] spec(mapgen-studio): design-system foundation changeset + decisions OpenSpec change 'mapgen-studio-design-system' (P1, FIRST domino): - proposal/tasks/design + mapgen-studio spec deltas (single tokenized design system; felt substrate-elevation hierarchy; single .dark theming + contour focus). - .interface-design/system.md: confirmed direction (cartographer's instrument, elevated cool-steel slate, substrate elevation = primary craft lever, dark-first + real light). launch.json for live preview. Co-Authored-By: Claude Opus 4.8 --- .claude/launch.json | 12 ++ .../mapgen-studio/.interface-design/system.md | 149 ++++++++++++++++++ .../.openspec.yaml | 3 + .../mapgen-studio-design-system/design.md | 44 ++++++ .../mapgen-studio-design-system/proposal.md | 52 ++++++ .../specs/mapgen-studio/spec.md | 59 +++++++ .../mapgen-studio-design-system/tasks.md | 42 +++++ 7 files changed, 361 insertions(+) create mode 100644 .claude/launch.json create mode 100644 apps/mapgen-studio/.interface-design/system.md create mode 100644 openspec/changes/mapgen-studio-design-system/.openspec.yaml create mode 100644 openspec/changes/mapgen-studio-design-system/design.md create mode 100644 openspec/changes/mapgen-studio-design-system/proposal.md create mode 100644 openspec/changes/mapgen-studio-design-system/specs/mapgen-studio/spec.md create mode 100644 openspec/changes/mapgen-studio-design-system/tasks.md diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000000..54bf2386f9 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,12 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "mapgen-studio", + "runtimeExecutable": "bun", + "runtimeArgs": ["run", "dev"], + "cwd": "apps/mapgen-studio", + "port": 5173 + } + ] +} diff --git a/apps/mapgen-studio/.interface-design/system.md b/apps/mapgen-studio/.interface-design/system.md new file mode 100644 index 0000000000..b74068fc5d --- /dev/null +++ b/apps/mapgen-studio/.interface-design/system.md @@ -0,0 +1,149 @@ +# MapGen Studio — Design System (decisions) + +> **This file holds DECISIONS, not extraction.** The as-built baseline lives in +> [`../system.md`](../system.md). When they disagree, this file wins — it is the +> target the redesign realizes. Confirmed with the user 2026-06-08. Downstream +> slices and agents APPLY this; they do not re-litigate it. +> +> Realized + value-tuned in the P1 implementation slice (Tailwind v4 + shadcn). +> Starting token values below are the committed intent; exact final values are +> validated against the running app during P1. + +## Direction + +**Cartographer's instrument.** A dense, dark-first precision tool for *sculpting a +world by adjusting generation parameters and reading the map*. The deck.gl map is +the hero and carries all terrain color; the chrome is a recessed graphite +instrument bezel that never competes with it. **The instrument makes way for the +matter:** it lives in its own robust substrate layers but never steals the show. +Keep the existing DNA — dense, border-tiered, micro-typography, a single restrained +cool accent — but make every choice intentional. + +**Accent decision (user-confirmed, refined 2026-06-08):** the accent is an +**elevated cool-steel slate**, not a vivid hue. Teal/cyan was considered and set +aside *because the map is the matter* — a saturated accent would pull the eye to +the chrome. The commodity tell today is not "slate" itself; it is that slate is +*unintentional* (declared indigo, used gray, no system). The fix is to make slate +**deliberate and robust**: a committed cool-steel hue with a defined ramp, a +rigorous substrate-elevation system, and a contour signature carried by +**luminance** rather than color. Craft, not chroma. + +- **Who:** a Civ7 map-mod author in long, dark, tweak-and-observe sessions. +- **Verb:** tune a recipe → watch the world respond → run it in-game. +- **Feel:** quiet, technical, precise. Like a DAW / topographic survey console. +- **Light mode:** dark-first, but ship a genuinely correct light theme via + shadcn's single `.dark` class strategy (one switch, no prop drilling). + +## Signature + +The **contour line**, carried by **luminance, not a loud hue**. The map domain's +defining mark (topographic contour / graticule) becomes the interface's signature: + +- **Focus** renders as a 1px *contour ring* in a brighter cool-steel slate — a + luminance step above the chrome, not a saturated glow block. +- **Active/selected** states use a thin steel rule/underline (a contour), not a + filled slab — fills are reserved for the one primary action, itself a restrained + elevated slate. +- **Hypsometric mini-legends:** elevation/rainfall/biome readouts use real + cartographic ramps (relief greens→tans→snow; bathymetric blues) so data viz + belongs to the product's world. This is the one place real color appears in the + chrome — and it appears as *the matter*, the same domain color the map carries, + not as instrument identity. + +## Primary craft lever (from the live app, 2026-06-08) + +A screenshot of the running app revealed the real departure is **not color** — it +is **flatness**. Page, panel, and nested surfaces sit ~4% lightness apart with no +floating-layer shadows, so borders carry all structure and the UI reads as one +hollow dark plane (the squint test shows almost no hierarchy). The empty map +center reads as *dead space*, not *a stage awaiting matter*. + +So P1's highest-value work, in order: +1. **Robust substrate elevation** — widen the (still whisper-quiet) page→panel→ + nested lightness tiers + a real border progression so structure is *felt*. This + is what makes a neutral slate instrument feel machined instead of flat. +2. Elevate the slate as quiet identity (committed steel hue, contour-luminance focus). +3. Make "awaiting matter" intentional — the empty stage frames the map + (graticule/contour), reads as ready, not hollow. +4. Under the hood: named tokens, real overlay motion, `.dark`-class theming repair. + +## Foundation & depth + +- **Foundation:** cool graphite/ink, near-monochrome. Same hue across surfaces; + hierarchy by lightness step only (never different hues per surface). +- **Depth:** **borders-only**, tiered by background lightness step + 1px hairline. + This substrate-elevation system is where the craft lives — the layering must be + *felt, not seen* (whisper-quiet lightness steps; the squint test must still show + hierarchy with nothing jumping out). A robust tier system is what makes a neutral + slate read as intentional rather than flat. Shadows appear ONLY on floating + layers (dialog/popover/toast). `backdrop-blur` on chrome that floats over the + map. Do not mix depth strategies. +- **Accent:** ONE elevated cool-steel slate, used with restraint (primary action + + active/focus contours). It replaces the commodity declared-indigo-but-used-gray + non-choice by being *committed and systematic*, not by being louder. + +## Tokens (HSL, shadcn `:root` / `.dark`) + +Dark is the default app theme. Values are the committed starting point. + +| Token | Dark | Light | Role | +|---|---|---|---| +| `--background` | `240 20% 5%` | `240 20% 96%` | page / deck.gl backdrop tint | +| `--foreground` | `240 14% 92%` | `215 28% 17%` | primary text | +| `--card` | `240 9% 9%` | `0 0% 100%` | panels, headers | +| `--card-foreground` | `240 14% 92%` | `215 28% 17%` | | +| `--popover` | `240 8% 11%` | `0 0% 100%` | one tier above card (dropdowns) | +| `--popover-foreground` | `240 14% 92%` | `215 28% 17%` | | +| `--muted` | `240 8% 13%` | `240 14% 93%` | hover/active surface, tags | +| `--muted-foreground` | `240 6% 56%` | `220 9% 46%` | secondary/label text | +| `--primary` | `216 18% 42%` | `216 22% 32%` | elevated cool-steel slate accent | +| `--primary-foreground` | `210 20% 96%` | `0 0% 100%` | text on primary | +| `--border` | `240 9% 18%` | `220 13% 91%` | workhorse hairline | +| `--input` | `240 9% 25%` | `220 13% 86%` | control border | +| `--ring` | `212 28% 60%` | `216 25% 45%` | focus contour (luminance step, not chroma) | +| `--destructive` | `350 70% 58%` | `350 72% 48%` | rose (commit; kill rose/red drift) | +| `--destructive-foreground` | `0 0% 98%` | `0 0% 100%` | | +| `--radius` | `0.25rem` | — | base; card = `0.5rem` | + +Secondary text tiers: a tertiary/muted level (`240 7% 38%` dark) for placeholders/ +help. Semantic (used sparingly, slightly desaturated in dark): success +`160 55% 45%`, warning `40 80% 55%`, info = the steel-slate accent (no separate +info hue — keep the chrome monochrome-cool). + +## Type + +- **Sans:** Inter. **Mono:** JetBrains Mono (tabular numerals for data). Self-host + (remove render-blocking Google Fonts `@import`). +- **Named scale (replace ad-hoc `text-[11px]`):** `text-data` 11px / `text-label` + 10px uppercase `tracking-wider` / `text-xs` 12px / `text-sm` 14px (dialog + titles). Weights: medium (500) default emphasis, semibold (600) headers. No bold + in chrome. 11px is the body default; 10px the eyebrow label. + +## Spacing / radius / motion + +- **Spacing base 4px.** Scale: 4, 6, 8, 10, 12, 16. Field rows `py-1`, label↔input + `gap-3`. Cards `p-2.5`. Nested `p-2`. +- **Radius:** 4px inputs/buttons/tags, 8px cards/dialogs/dropdowns, full for pills/ + switch. (Drop the 6px/`xl`/`2xl` noise.) +- **Motion:** real, quiet, deceleration easing — install the Tailwind v4 animate + layer so the (currently dead) `animate-in` overlay transitions actually run. + 150ms fast / 200ms normal. No spring/bounce. `animate-pulse-subtle` stays for + running state. + +## Theming mechanism (the repair) + +- Single **`.dark` class** on `` (shadcn strategy). Set Tailwind + `darkMode: 'class'` (v4 equivalent). One theme switch writes the class. +- **Delete** `createTheme()` runtime string interpolation (JIT-invisible — a real + bug), the `lightMode` boolean prop threaded through ~24 files, the duplicate + `colors.light|dark` config, and the dead `--spacing-*`/`--radius-*` vars. The + `lightMode`-prop removal lands as components migrate (P3/P4); P1 establishes the + `.dark` system and tokens so both can briefly coexist without conflict. + +## Component patterns (preserve as-built dimensions, re-skin onto shadcn) + +Keep the dense dimensions from the extraction baseline — Button `h-8`/`h-7`, Input +`h-7`, Switch 36×20, Field row `justify-between gap-3 py-1` with `min-w-[96px]` +label, Card `rounded-lg border p-2.5`, eyebrow label uppercase 10px. The migration +changes the *mechanism* (Radix shadcn primitives, token-driven) and adds the +contour-focus signature + motion — not the density. diff --git a/openspec/changes/mapgen-studio-design-system/.openspec.yaml b/openspec/changes/mapgen-studio-design-system/.openspec.yaml new file mode 100644 index 0000000000..39ea9f20ca --- /dev/null +++ b/openspec/changes/mapgen-studio-design-system/.openspec.yaml @@ -0,0 +1,3 @@ +schema: spec-driven +created: 2026-06-08 +status: draft diff --git a/openspec/changes/mapgen-studio-design-system/design.md b/openspec/changes/mapgen-studio-design-system/design.md new file mode 100644 index 0000000000..328eb4bc64 --- /dev/null +++ b/openspec/changes/mapgen-studio-design-system/design.md @@ -0,0 +1,44 @@ +## Context + +Foundation slice (P1) of the MapGen Studio redesign. Controlling frame: +`docs/projects/mapgen-studio-redesign/FRAME.md`. Confirmed design direction and +token values: `apps/mapgen-studio/.interface-design/system.md` (decisions) over +`apps/mapgen-studio/system.md` (as-built extraction). + +## Key decisions + +- **Tailwind v4, CSS-first.** React 19.2 + Vite 7 already; the v3 config is largely + dead. v4's `@theme` + `@tailwindcss/vite` removes the PostCSS/autoprefixer/ + config surface. Existing arbitrary hex utilities (`bg-[#141418]`, `text-[11px]`) + are valid in v4 and render unchanged — this is what makes a non-breaking + foundation possible. +- **Additive, non-breaking.** This slice adds the token + shadcn + `.dark` layer + alongside the existing hex styling. Components are NOT re-skinned here; the old + `lightMode`/`createTheme` path is retired later as components migrate onto + tokens (P3/P4). Coexistence is deliberate and bounded. +- **Substrate elevation is the primary craft lever.** The live app reads flat + (page/panel/nested ~4% apart, no floating shadows). The token system encodes a + felt elevation tier scale + border progression so structure is perceived without + anything shouting (squint test is the gate). This is where a neutral slate + becomes intentional. +- **Accent = elevated cool-steel slate, not chroma.** One restrained accent + (`--primary 216 18% 42%`); the contour focus signature is a luminance step + (`--ring 212 28% 60%`), not a saturated glow. Teal was considered and set aside + because the deck.gl map is the only thing that should carry color. +- **`.dark` class theming.** Single switch on ``, no prop drilling, no-flash + bootstrap. Replaces the OS-`prefers-color-scheme` + JIT-invisible `createTheme()` + mechanism. + +## Risks / mitigations + +- *v4 migration regresses rendering* → existing hex utilities are v4-valid; verify + by build + live screenshot diff before stacking. Legacy `--color-*` vars + re-pointed at new tokens so focus/scrollbar/select keep working. +- *Two theming systems coexist* → bounded and intentional; `.dark` is + authoritative now, `lightMode` removed as components migrate. +- *Monorepo build coupling* → all changed config is app-local to `apps/mapgen-studio`. + +## Verification + +`tsc --noEmit` + `vite build` + worker-bundle check; live screenshot in dark and +light. Behavior parity: no component logic touched. diff --git a/openspec/changes/mapgen-studio-design-system/proposal.md b/openspec/changes/mapgen-studio-design-system/proposal.md new file mode 100644 index 0000000000..c2bca50b45 --- /dev/null +++ b/openspec/changes/mapgen-studio-design-system/proposal.md @@ -0,0 +1,52 @@ +## Why + +MapGen Studio's UI has no design system. Color lives in 242 arbitrary hex +utilities across components; three disagreeing dark palettes exist +(`tailwind.config.js`, `src/index.css`, `src/ui/hooks/useTheme.ts`); the declared +accent (indigo `#5e5ce6`) is not the used accent (slate `#4b5563`); theming is +structurally broken (`createTheme()` builds JIT-invisible runtime classes, and a +`lightMode` prop threaded through ~24 files fights OS `prefers-color-scheme`); the +substrate is flat (page/panel/nested ~4% apart, no felt hierarchy); and overlay +motion classes (`animate-in`) are dead (no keyframes installed). + +This is the FIRST domino of the MapGen Studio redesign (see +`docs/projects/mapgen-studio-redesign/FRAME.md`). Every later component slice +consumes the system this change establishes. Direction is user-confirmed and +recorded in `apps/mapgen-studio/.interface-design/system.md`: a dense, dark-first +**cartographer's instrument** with an elevated cool-steel **slate** accent, a +robust **substrate-elevation** system as the primary craft lever, and a +**contour-line** focus signature carried by luminance, not chroma. + +## What Changes + +- Migrate the studio build from Tailwind v3 (config + PostCSS) to **Tailwind v4** + (CSS-first `@theme`, `@tailwindcss/vite`), removing `tailwind.config.js`, + `postcss.config.js`, and `autoprefixer`. +- Establish a single **tokenized design system**: HSL design tokens in `:root` + + `.dark`, including a felt substrate-elevation tier scale and a border + progression; map them through `@theme` to Tailwind utilities and shadcn aliases. +- Initialize **canonical shadcn/ui** scaffolding: `components.json`, `lib/utils.ts` + (`cn`), and the New York / CSS-vars / slate base configuration. (Primitive + components are migrated in a later change.) +- Repair theming: adopt shadcn's single **`.dark` class** strategy with a + no-flash bootstrap (default dark), replacing the `prefers-color-scheme` + dead + `createTheme()` mechanism. The legacy `lightMode` prop path is removed as + components migrate (later changes); this change makes the `.dark` system + authoritative and lets both coexist without conflict. +- Install a real overlay **motion** layer so the previously dead `animate-in` + transitions run; commit the single accent and self-host fonts. + +This change is **foundation only**. It does not re-skin existing components or +change their behavior; their 242 arbitrary hex classes keep rendering unchanged +until later migration slices retire them. + +## Affected Owners + +- `apps/mapgen-studio` (build config, `src/index.css`, `src/lib/utils.ts`, + `components.json`, theme bootstrap) + +## Out Of Scope + +- Component primitive migration to shadcn, rjsf re-skin, App.tsx decomposition, + client-state/server-data rework, and the studio server — each its own later + change in the redesign workstream. diff --git a/openspec/changes/mapgen-studio-design-system/specs/mapgen-studio/spec.md b/openspec/changes/mapgen-studio-design-system/specs/mapgen-studio/spec.md new file mode 100644 index 0000000000..724903f631 --- /dev/null +++ b/openspec/changes/mapgen-studio-design-system/specs/mapgen-studio/spec.md @@ -0,0 +1,59 @@ +## ADDED Requirements + +### Requirement: Studio UI Uses A Single Tokenized Design System + +MapGen Studio SHALL express interface color, elevation, border, radius, and focus +through a single set of named design tokens defined once in `:root` (light) and +`.dark` (dark), rather than per-component hardcoded values or multiple disagreeing +palettes. + +#### Scenario: Tokens are the source of truth + +- **WHEN** the design system is established +- **THEN** background, foreground, card, popover, muted, primary, border, input, + ring, destructive, and radius are defined as HSL tokens in `:root` and `.dark` +- **AND** a single committed accent (elevated cool-steel slate) is used for + primary action and active/focus states +- **AND** the legacy `--color-*` variables referenced by focus, scrollbar, and + select styles resolve to those same tokens + +#### Scenario: Foundation does not break existing components + +- **WHEN** the token + Tailwind v4 + shadcn foundation is added +- **THEN** components still styled with arbitrary hex utilities render unchanged +- **AND** no component behavior is modified by this change + +### Requirement: Studio Surfaces Use A Felt Substrate-Elevation Hierarchy + +The design system SHALL encode a substrate-elevation tier scale and a border +progression such that page, panel, nested, and floating surfaces are perceivable +as a hierarchy without any element visually shouting. + +#### Scenario: Hierarchy survives the squint test + +- **WHEN** surfaces are rendered using the elevation tokens +- **THEN** page → panel → nested → floating each occupy a distinct (whisper-quiet) + lightness tier +- **AND** floating layers (dialog, popover, toast) carry shadow while in-page + surfaces rely on the border progression +- **AND** structure remains perceivable when interface detail is blurred + +### Requirement: Studio Theming Uses A Single `.dark` Class Strategy + +MapGen Studio SHALL select light or dark theme via a single `.dark` class on the +document root, applied before first paint, rather than OS `prefers-color-scheme` +coupling or runtime-interpolated theme classes. + +#### Scenario: Theme is deterministic and flash-free + +- **WHEN** the app loads +- **THEN** the stored theme (default dark) is applied to the document root before + first paint +- **AND** toggling the theme switches the `.dark` class with no per-component prop + threading + +#### Scenario: Focus renders as a contour signature + +- **WHEN** an interactive element receives keyboard focus +- **THEN** focus renders as a 1px contour ring using the ring token (a luminance + step above the chrome), not a saturated glow block diff --git a/openspec/changes/mapgen-studio-design-system/tasks.md b/openspec/changes/mapgen-studio-design-system/tasks.md new file mode 100644 index 0000000000..5d0083dede --- /dev/null +++ b/openspec/changes/mapgen-studio-design-system/tasks.md @@ -0,0 +1,42 @@ +## 1. Tailwind v4 migration + +- [ ] 1.1 Add `tailwindcss@4`, `@tailwindcss/vite`, `tw-animate-css`; remove + `autoprefixer`. Add the `@tailwindcss/vite` plugin to `vite.config.ts` (do not + touch the existing `/api` middleware). +- [ ] 1.2 Delete `tailwind.config.js` and `postcss.config.js` (CSS-first v4); fold + the still-live bits (font families, `pulse-subtle`) into CSS. +- [ ] 1.3 Rewrite `src/index.css` head to `@import "tailwindcss"` + + `@import "tw-animate-css"` + `@custom-variant dark`. + +## 2. Token system + +- [ ] 2.1 Define the cartographer's-instrument HSL tokens in `:root` (light) and + `.dark` (dark) per `apps/mapgen-studio/.interface-design/system.md`: background/ + foreground, card/popover, muted, primary (elevated steel slate), + primary-foreground, border/input, ring (contour luminance), destructive, radius. +- [ ] 2.2 Add the felt **substrate-elevation** tier scale (page→panel→nested→ + floating) and a **border progression** (subtle/default/strong) — the primary + craft lever; widen the lightness steps so the squint test shows hierarchy. +- [ ] 2.3 Map tokens through `@theme inline` to Tailwind color/radius utilities and + shadcn aliases. Preserve the legacy `--color-*` vars (focus/scrollbar/select + still reference them) by re-pointing them at the new tokens. + +## 3. shadcn scaffolding + +- [ ] 3.1 Add `components.json` (New York, CSS vars, base color slate, path + aliases) and `src/lib/utils.ts` exporting `cn` (clsx + tailwind-merge). + +## 4. Theming repair + craft foundation + +- [ ] 4.1 Add a no-flash `.dark` bootstrap (inline head script, default dark, + localStorage-backed) and make `.dark` authoritative; keep a + `prefers-color-scheme` fallback for first paint during coexistence. +- [ ] 4.2 Replace the global `:focus-visible` outline with the contour-ring focus + signature (ring token, luminance step). Self-host Inter + JetBrains Mono + (remove the render-blocking Google Fonts `@import`). + +## 5. Verify + +- [ ] 5.1 `tsc --noEmit` clean; `vite build` succeeds; worker-bundle check passes. +- [ ] 5.2 App renders in dark and in light (via `.dark` toggle) with no regression + to existing hex-styled components; capture a screenshot diff of the live app. From a0240ac7458eb0ee4fb655851accf5f4e0feb045 Mon Sep 17 00:00:00 2001 From: Matei Date: Mon, 8 Jun 2026 23:45:00 -0400 Subject: [PATCH 2/3] =?UTF-8?q?feat(mapgen-studio):=20design-system=20foun?= =?UTF-8?q?dation=20=E2=80=94=20Tailwind=20v4=20+=20shadcn=20+=20tokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements OpenSpec change 'mapgen-studio-design-system' (P1, non-breaking foundation): - Tailwind v3.4 → v4 (CSS-first): @tailwindcss/vite plugin, delete tailwind.config.js + postcss.config.js + autoprefixer; src/index.css → @import tailwindcss + tw-animate-css. - Single tokenized design system: HSL tokens in :root/.dark mapped via @theme inline. Cartographer's-instrument direction — graphite substrate, one elevated cool-steel slate accent, FELT substrate-elevation tiers (page→panel→floating) + border progression (the primary craft lever), contour-luminance focus ring. - shadcn scaffolding: components.json (new-york, slate, css vars), src/lib/utils.ts (cn), @/* alias in tsconfig + vite. - Theming repair: single .dark class with no-flash pre-paint bootstrap (default dark), replacing prefers-color-scheme + JIT-invisible createTheme. Self-hosted Inter + JetBrains Mono (@fontsource), removing render-blocking Google Fonts. Real overlay motion via tw-animate-css. Additive: existing components keep their hex utilities and render unchanged; they adopt tokens in later slices. Verify: tsc clean; vite build + worker-bundle check pass; live dev renders with no console errors and no regression. Co-Authored-By: Claude Opus 4.8 --- apps/mapgen-studio/components.json | 21 ++ apps/mapgen-studio/index.html | 21 +- apps/mapgen-studio/package.json | 8 +- apps/mapgen-studio/postcss.config.js | 6 - apps/mapgen-studio/src/index.css | 319 ++++++++++-------- apps/mapgen-studio/src/lib/utils.ts | 11 + apps/mapgen-studio/src/main.tsx | 7 + apps/mapgen-studio/tailwind.config.js | 71 ---- apps/mapgen-studio/tsconfig.json | 4 + apps/mapgen-studio/vite.config.ts | 2 + bun.lock | 126 +++++-- .../mapgen-studio-design-system/tasks.md | 26 +- 12 files changed, 354 insertions(+), 268 deletions(-) create mode 100644 apps/mapgen-studio/components.json delete mode 100644 apps/mapgen-studio/postcss.config.js create mode 100644 apps/mapgen-studio/src/lib/utils.ts delete mode 100644 apps/mapgen-studio/tailwind.config.js diff --git a/apps/mapgen-studio/components.json b/apps/mapgen-studio/components.json new file mode 100644 index 0000000000..622679107e --- /dev/null +++ b/apps/mapgen-studio/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/apps/mapgen-studio/index.html b/apps/mapgen-studio/index.html index fd4591b925..1a1a5ae9b0 100644 --- a/apps/mapgen-studio/index.html +++ b/apps/mapgen-studio/index.html @@ -4,16 +4,31 @@ MapGen Studio + diff --git a/apps/mapgen-studio/package.json b/apps/mapgen-studio/package.json index c704888489..605341481c 100644 --- a/apps/mapgen-studio/package.json +++ b/apps/mapgen-studio/package.json @@ -21,6 +21,8 @@ "@civ7/plugin-mods": "workspace:*", "@civ7/studio-server": "workspace:*", "@deck.gl/core": "^9.0.43", + "@fontsource/inter": "^5.1.1", + "@fontsource/jetbrains-mono": "^5.1.1", "@deck.gl/layers": "^9.0.43", "@deck.gl/react": "^9.0.43", "@orpc/client": "^1.14.4", @@ -44,12 +46,12 @@ "typebox": "^1.0.80" }, "devDependencies": { + "@tailwindcss/vite": "^4.1.13", "@types/react": "^19.2.10", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.2", - "autoprefixer": "latest", - "postcss": "latest", - "tailwindcss": "3.4.17", + "tailwindcss": "^4.1.13", + "tw-animate-css": "^1.4.0", "typescript": "^5.9.3", "vite": "^7.3.1" } diff --git a/apps/mapgen-studio/postcss.config.js b/apps/mapgen-studio/postcss.config.js deleted file mode 100644 index 2aa7205d4b..0000000000 --- a/apps/mapgen-studio/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -}; diff --git a/apps/mapgen-studio/src/index.css b/apps/mapgen-studio/src/index.css index 422c21e9d5..07a1419b7b 100644 --- a/apps/mapgen-studio/src/index.css +++ b/apps/mapgen-studio/src/index.css @@ -1,138 +1,210 @@ - /* ============================================================================ - Font Imports - ============================================================================ */ -@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"); - -/* ============================================================================ - Tailwind Base - ============================================================================ */ -@tailwind base; -@tailwind components; -@tailwind utilities; - -/* ============================================================================ - CSS Variables (Theme Tokens) + MapGen Studio — design system foundation (Tailwind v4, CSS-first) + Direction + token rationale: apps/mapgen-studio/.interface-design/system.md + A dense, dark-first "cartographer's instrument": graphite substrate, one + elevated cool-steel slate accent, a felt elevation hierarchy, and a + contour-line focus signature carried by luminance, not chroma. ============================================================================ */ +@import "tailwindcss"; +@import "tw-animate-css"; + +/* Dark is selected by a single `.dark` class on (set pre-paint in + index.html). No prefers-color-scheme coupling, no per-component prop. */ +@custom-variant dark (&:is(.dark *)); + +/* ---------------------------------------------------------------------------- + Token → utility mapping. `inline` keeps the var reference in generated CSS so + flipping `.dark` re-themes at runtime. Custom tokens (border-subtle/-strong, + input-background, elevation-*, surface-sunken, success, warning) carry the + substrate-elevation craft the stock shadcn set doesn't express. + ---------------------------------------------------------------------------- */ +@theme inline { + --color-background: hsl(var(--background)); + --color-foreground: hsl(var(--foreground)); + --color-card: hsl(var(--card)); + --color-card-foreground: hsl(var(--card-foreground)); + --color-popover: hsl(var(--popover)); + --color-popover-foreground: hsl(var(--popover-foreground)); + --color-primary: hsl(var(--primary)); + --color-primary-foreground: hsl(var(--primary-foreground)); + --color-secondary: hsl(var(--secondary)); + --color-secondary-foreground: hsl(var(--secondary-foreground)); + --color-muted: hsl(var(--muted)); + --color-muted-foreground: hsl(var(--muted-foreground)); + --color-accent: hsl(var(--accent)); + --color-accent-foreground: hsl(var(--accent-foreground)); + --color-destructive: hsl(var(--destructive)); + --color-destructive-foreground: hsl(var(--destructive-foreground)); + --color-border: hsl(var(--border)); + --color-input: hsl(var(--input)); + --color-ring: hsl(var(--ring)); + + /* substrate elevation + semantics (the primary craft lever) */ + --color-border-subtle: hsl(var(--border-subtle)); + --color-border-strong: hsl(var(--border-strong)); + --color-input-background: hsl(var(--input-background)); + --color-elevation-1: hsl(var(--elevation-1)); + --color-elevation-2: hsl(var(--elevation-2)); + --color-surface-sunken: hsl(var(--surface-sunken)); + --color-success: hsl(var(--success)); + --color-warning: hsl(var(--warning)); + + --font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace; + + --radius-sm: 0.25rem; /* 4px — inputs, buttons, tags (the default) */ + --radius-md: 0.375rem; /* 6px — rare */ + --radius-lg: 0.5rem; /* 8px — cards, dialogs, dropdowns */ + --radius-xl: 0.75rem; + + --animate-pulse-subtle: pulse-subtle 2s ease-in-out infinite; +} + +/* ---------------------------------------------------------------------------- + Tokens. Values are HSL channels (H S% L%). Dark is the product default; the + ladder below is intentionally a felt hierarchy — page → panel → floating each + occupy a distinct (still quiet) lightness tier so structure survives the + squint test instead of reading as one hollow plane. + ---------------------------------------------------------------------------- */ :root { - /* Light mode colors */ - --color-bg-primary: #f5f5f7; - --color-bg-secondary: #ffffff; - --color-bg-tertiary: #f0f0f2; - - --color-text-primary: #1f2933; - --color-text-secondary: #6b7280; - --color-text-muted: #9ca3af; - - --color-border-primary: #e5e7eb; - --color-border-secondary: #d1d5db; - - --color-accent: #4b5563; - --color-accent-hover: #374151; - - /* Spacing */ - --spacing-xs: 4px; - --spacing-sm: 8px; - --spacing-md: 16px; - --spacing-lg: 24px; - --spacing-xl: 32px; - - /* Border radius */ - --radius-sm: 4px; - --radius-md: 6px; - --radius-lg: 8px; - --radius-xl: 12px; - - /* Transitions */ - --transition-fast: 150ms ease; - --transition-normal: 200ms ease; -} - -@media (prefers-color-scheme: dark) { - :root { - --color-bg-primary: #0a0a12; - --color-bg-secondary: #16161d; - --color-bg-tertiary: #1f1f28; - - --color-text-primary: #e2e2e9; - --color-text-secondary: #7a7a8c; - --color-text-muted: #6a6a7c; - - --color-border-primary: #26262e; - --color-border-secondary: #32323e; - - --color-accent: #5e5ce6; - --color-accent-hover: #7472f7; + color-scheme: light; + + --background: 240 20% 97%; + --foreground: 215 25% 17%; + --card: 0 0% 100%; + --card-foreground: 215 25% 17%; + --popover: 0 0% 100%; + --popover-foreground: 215 25% 17%; + --primary: 216 22% 32%; + --primary-foreground: 0 0% 100%; + --secondary: 240 14% 94%; + --secondary-foreground: 215 25% 22%; + --muted: 240 14% 94%; + --muted-foreground: 220 9% 46%; + --accent: 240 14% 92%; + --accent-foreground: 215 25% 17%; + --destructive: 350 72% 48%; + --destructive-foreground: 0 0% 100%; + --border: 220 13% 88%; + --input: 220 13% 84%; + --ring: 216 25% 45%; + + --border-subtle: 220 14% 92%; + --border-strong: 220 13% 78%; + --input-background: 0 0% 100%; + --elevation-1: 0 0% 100%; + --elevation-2: 0 0% 100%; + --surface-sunken: 240 16% 95%; + --success: 160 55% 36%; + --warning: 38 80% 42%; + + --radius: 0.25rem; + + /* legacy vars still referenced by scrollbar/select below — repointed to tokens */ + --color-border-secondary: hsl(var(--border-strong)); + --color-text-muted: hsl(var(--muted-foreground)); +} + +.dark { + color-scheme: dark; + + --background: 240 14% 6%; /* page — the deck.gl canvas backdrop */ + --foreground: 240 13% 92%; + --card: 240 9% 10%; /* panel — sits a felt step above the page */ + --card-foreground: 240 13% 92%; + --popover: 240 9% 13%; /* floating layers — a step above panels */ + --popover-foreground: 240 13% 92%; + --primary: 216 18% 44%; /* elevated cool-steel slate — the one accent */ + --primary-foreground: 210 20% 96%; + --secondary: 240 7% 15%; + --secondary-foreground: 240 12% 88%; + --muted: 240 7% 15%; + --muted-foreground: 240 5% 58%; + --accent: 240 8% 17%; /* ghost/hover surface */ + --accent-foreground: 240 13% 92%; + --destructive: 350 70% 58%; + --destructive-foreground: 0 0% 98%; + --border: 240 8% 22%; /* findable hairline against panels */ + --input: 240 9% 26%; + --ring: 212 28% 62%; /* focus contour — a luminance step, not a hue */ + + --border-subtle: 240 8% 15%; /* dividers within a surface */ + --border-strong: 240 9% 32%; /* emphasis / control focus border */ + --input-background: 240 12% 8%; /* inset: darker than its panel */ + --elevation-1: 240 9% 10%; /* = panel */ + --elevation-2: 240 9% 13%; /* = floating / raised-nested */ + --surface-sunken: 240 12% 7%; /* nested cards: between page and panel */ + --success: 160 50% 46%; + --warning: 40 80% 56%; +} + +/* ---------------------------------------------------------------------------- + Base + ---------------------------------------------------------------------------- */ +@layer base { + * { + border-color: hsl(var(--border)); } -} -/* ============================================================================ - Base Styles - ============================================================================ */ -html { - font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} + html { + font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } -body { - margin: 0; - padding: 0; - min-height: 100vh; + body { + margin: 0; + padding: 0; + min-height: 100vh; + background-color: hsl(var(--background)); + color: hsl(var(--foreground)); + } + + /* Signature: focus is a 1px luminous contour ring (the ring token), offset + so it reads as a drawn contour rather than a filled glow. */ + *:focus { + outline: none; + } + *:focus-visible { + outline: 1px solid hsl(var(--ring)); + outline-offset: 2px; + } } -/* Mono font for code/data */ .font-mono { - font-family: "JetBrains Mono", "SF Mono", "Fira Code", monospace; + font-family: var(--font-mono); } -/* ============================================================================ - Custom Scrollbar - ============================================================================ */ +/* ---------------------------------------------------------------------------- + Custom scrollbar — quiet by default, findable on hover. + ---------------------------------------------------------------------------- */ .custom-scrollbar { scrollbar-width: thin; scrollbar-color: var(--color-border-secondary) transparent; } - .custom-scrollbar::-webkit-scrollbar { width: 6px; height: 6px; } - .custom-scrollbar::-webkit-scrollbar-track { background: transparent; border-radius: 3px; } - .custom-scrollbar::-webkit-scrollbar-thumb { background-color: var(--color-border-secondary); border-radius: 3px; border: 1px solid transparent; } - .custom-scrollbar::-webkit-scrollbar-thumb:hover { background-color: var(--color-text-muted); } - .custom-scrollbar:not(:hover)::-webkit-scrollbar-thumb { background-color: transparent; } -/* ============================================================================ - Focus Styles - ============================================================================ */ -*:focus { - outline: none; -} - -*:focus-visible { - outline: 2px solid var(--color-accent); - outline-offset: 2px; -} - -/* ============================================================================ - Form Element Resets - ============================================================================ */ +/* ---------------------------------------------------------------------------- + Form element resets (kept from the as-built baseline) + ---------------------------------------------------------------------------- */ select { appearance: none; background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); @@ -141,23 +213,28 @@ select { background-size: 1.25em 1.25em; padding-right: 2rem; } - input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } - input[type="number"] { -moz-appearance: textfield; } -/* ============================================================================ - Utility Classes - ============================================================================ */ +button { + cursor: pointer; +} +button:disabled, +select:disabled, +input:disabled { + cursor: not-allowed; + opacity: 0.5; +} + @supports not (backdrop-filter: blur(8px)) { .backdrop-blur-sm { - background-color: rgba(255, 255, 255, 0.95); + background-color: hsl(var(--popover) / 0.95); } } @@ -170,36 +247,6 @@ input[type="number"] { opacity: 0.7; } } - .animate-pulse-subtle { animation: pulse-subtle 2s ease-in-out infinite; } - -.transition-colors { - transition-property: color, background-color, border-color; - transition-timing-function: ease; - transition-duration: 150ms; -} - -/* ============================================================================ - Component-specific Overrides - ============================================================================ */ -input[type="checkbox"] { - cursor: pointer; -} - -button { - cursor: pointer; - border: none; - background: none; - padding: 0; - font: inherit; -} - -button:disabled, -select:disabled, -input:disabled { - cursor: not-allowed; - opacity: 0.5; -} - diff --git a/apps/mapgen-studio/src/lib/utils.ts b/apps/mapgen-studio/src/lib/utils.ts new file mode 100644 index 0000000000..59b04332e5 --- /dev/null +++ b/apps/mapgen-studio/src/lib/utils.ts @@ -0,0 +1,11 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +/** + * Merge class names with Tailwind-aware conflict resolution. + * The canonical shadcn helper: `clsx` resolves conditionals, `twMerge` ensures + * the last conflicting Tailwind utility wins (e.g. `px-2 px-3` → `px-3`). + */ +export function cn(...inputs: ClassValue[]): string { + return twMerge(clsx(inputs)); +} diff --git a/apps/mapgen-studio/src/main.tsx b/apps/mapgen-studio/src/main.tsx index 0ab49baf08..6c4134ab2f 100644 --- a/apps/mapgen-studio/src/main.tsx +++ b/apps/mapgen-studio/src/main.tsx @@ -1,3 +1,10 @@ +// Self-hosted fonts (replaces the render-blocking Google Fonts @import). +import "@fontsource/inter/400.css"; +import "@fontsource/inter/500.css"; +import "@fontsource/inter/600.css"; +import "@fontsource/inter/700.css"; +import "@fontsource/jetbrains-mono/400.css"; +import "@fontsource/jetbrains-mono/500.css"; import "./index.css"; import { StrictMode } from "react"; diff --git a/apps/mapgen-studio/tailwind.config.js b/apps/mapgen-studio/tailwind.config.js deleted file mode 100644 index 13e0624026..0000000000 --- a/apps/mapgen-studio/tailwind.config.js +++ /dev/null @@ -1,71 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -export default { - content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], - theme: { - extend: { - fontFamily: { - sans: ["Inter", "-apple-system", "BlinkMacSystemFont", "Segoe UI", "Roboto", "sans-serif"], - mono: ["JetBrains Mono", "SF Mono", "Fira Code", "monospace"], - }, - colors: { - light: { - bg: { - primary: "#f5f5f7", - secondary: "#ffffff", - tertiary: "#f0f0f2", - }, - text: { - primary: "#1f2933", - secondary: "#6b7280", - muted: "#9ca3af", - }, - border: { - primary: "#e5e7eb", - secondary: "#d1d5db", - }, - accent: "#4b5563", - }, - dark: { - bg: { - primary: "#0a0a12", - secondary: "#16161d", - tertiary: "#1f1f28", - }, - text: { - primary: "#e2e2e9", - secondary: "#7a7a8c", - muted: "#6a6a7c", - }, - border: { - primary: "#26262e", - secondary: "#32323e", - }, - accent: "#5e5ce6", - }, - }, - spacing: { - 18: "4.5rem", - 88: "22rem", - }, - borderRadius: { - xl: "0.75rem", - "2xl": "1rem", - }, - transitionDuration: { - 150: "150ms", - 200: "200ms", - }, - animation: { - "pulse-subtle": "pulse-subtle 2s ease-in-out infinite", - }, - keyframes: { - "pulse-subtle": { - "0%, 100%": { opacity: "1" }, - "50%": { opacity: "0.7" }, - }, - }, - }, - }, - plugins: [], -}; - diff --git a/apps/mapgen-studio/tsconfig.json b/apps/mapgen-studio/tsconfig.json index a36976f7b4..287b841e6a 100644 --- a/apps/mapgen-studio/tsconfig.json +++ b/apps/mapgen-studio/tsconfig.json @@ -7,6 +7,10 @@ "skipLibCheck": true, "moduleResolution": "bundler", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, "allowImportingTsExtensions": true, "isolatedModules": true, "moduleDetection": "force", diff --git a/apps/mapgen-studio/vite.config.ts b/apps/mapgen-studio/vite.config.ts index 8ff4a44da9..0f67e9fb29 100644 --- a/apps/mapgen-studio/vite.config.ts +++ b/apps/mapgen-studio/vite.config.ts @@ -1,5 +1,6 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; +import tailwindcss from "@tailwindcss/vite"; import { execFile } from "node:child_process"; import { createHash } from "node:crypto"; import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; @@ -1144,6 +1145,7 @@ function createStudioServerContextForApp(viteCommand: string): StudioServerConte export default defineConfig(({ command }) => ({ plugins: [ react(), + tailwindcss(), { name: "repo-backed-map-configs", configureServer(server) { diff --git a/bun.lock b/bun.lock index 321ba391af..bacf88cfa8 100644 --- a/bun.lock +++ b/bun.lock @@ -43,6 +43,8 @@ "@deck.gl/core": "^9.0.43", "@deck.gl/layers": "^9.0.43", "@deck.gl/react": "^9.0.43", + "@fontsource/inter": "^5.1.1", + "@fontsource/jetbrains-mono": "^5.1.1", "@orpc/client": "^1.14.4", "@orpc/contract": "^1.14.4", "@orpc/server": "^1.14.4", @@ -64,12 +66,12 @@ "typebox": "^1.0.80", }, "devDependencies": { + "@tailwindcss/vite": "^4.1.13", "@types/react": "^19.2.10", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.2", - "autoprefixer": "latest", - "postcss": "latest", - "tailwindcss": "3.4.17", + "tailwindcss": "^4.1.13", + "tw-animate-css": "^1.4.0", "typescript": "^5.9.3", "vite": "^7.3.1", }, @@ -622,6 +624,10 @@ "@floating-ui/utils": ["@floating-ui/utils@0.2.10", "", {}, "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ=="], + "@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="], + + "@fontsource/jetbrains-mono": ["@fontsource/jetbrains-mono@5.2.8", "", {}, "sha512-6w8/SG4kqvIMu7xd7wt6x3idn1Qux3p9N62s6G3rfldOUYHpWcc2FKrqf+Vo44jRvqWj2oAtTHrZXEP23oSKwQ=="], + "@hpcc-js/wasm": ["@hpcc-js/wasm@2.32.3", "", { "dependencies": { "yargs": "18.0.0" }, "bin": { "dot-wasm": "deprecated/dot-wasm.js" } }, "sha512-VyqR+nDpasbbCQceDTL7ioTSSi4AGxR9yrP+rFemvvC+fb6PYC20Xre9pDpM1QAiINP9ZUS15Z/c0hudlsB0+A=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -1114,6 +1120,36 @@ "@szmarczak/http-timer": ["@szmarczak/http-timer@5.0.1", "", { "dependencies": { "defer-to-connect": "^2.0.1" } }, "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw=="], + "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.3.0", "", { "dependencies": { "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "tailwindcss": "4.3.0" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw=="], + "@tanstack/query-core": ["@tanstack/query-core@5.101.0", "", {}, "sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow=="], "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], @@ -1304,8 +1340,6 @@ "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], - "autoprefixer": ["autoprefixer@10.5.0", "", { "dependencies": { "browserslist": "^4.28.2", "caniuse-lite": "^1.0.30001787", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong=="], - "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], "avsc": ["avsc@5.7.9", "", {}, "sha512-yOA4wFeI7ET3v32Di/sUybQ+ttP20JHSW3mxLuNGeO0uD6PPcvLrIQXSvy/rhJOWU5JrYh7U4OHplWMmtAtjMg=="], @@ -1334,7 +1368,7 @@ "base64id": ["base64id@2.0.0", "", {}, "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.32", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA=="], "basic-ftp": ["basic-ftp@5.1.0", "", {}, "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw=="], @@ -1352,7 +1386,7 @@ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + "browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], @@ -1384,7 +1418,7 @@ "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], - "caniuse-lite": ["caniuse-lite@1.0.30001793", "", {}, "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA=="], + "caniuse-lite": ["caniuse-lite@1.0.30001766", "", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="], "capital-case": ["capital-case@1.0.4", "", { "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case-first": "^2.0.2" } }, "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A=="], @@ -1594,7 +1628,7 @@ "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], - "electron-to-chromium": ["electron-to-chromium@1.5.361", "", {}, "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA=="], + "electron-to-chromium": ["electron-to-chromium@1.5.278", "", {}, "sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], @@ -1606,6 +1640,8 @@ "engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="], + "enhanced-resolve": ["enhanced-resolve@5.23.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA=="], + "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], "entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], @@ -1762,8 +1798,6 @@ "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], - "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], "front-matter": ["front-matter@4.0.2", "", { "dependencies": { "js-yaml": "^3.13.1" } }, "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg=="], @@ -2098,6 +2132,30 @@ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], + "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="], + + "lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], @@ -2336,7 +2394,7 @@ "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - "node-releases": ["node-releases@2.0.46", "", {}, "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ=="], + "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], "normalize-package-data": ["normalize-package-data@6.0.2", "", { "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g=="], @@ -2790,7 +2848,9 @@ "tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="], - "tailwindcss": ["tailwindcss@3.4.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og=="], + "tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="], + + "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="], "tar": ["tar@6.1.15", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A=="], @@ -2866,6 +2926,8 @@ "turbo-windows-arm64": ["turbo-windows-arm64@2.7.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-0wDVnUJLFAWm4ZzOQFDkbyyUqaszorTGf3Rdc22IRIyJTTLd6ajqdb+cWD89UZ1RKr953+PZR1gqgWQY4PDuhA=="], + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], + "tweezer.js": ["tweezer.js@1.5.0", "", {}, "sha512-aSiJz7rGWNAQq7hjMK9ZYDuEawXupcCWgl3woQQSoDP2Oh8O4srWb/uO1PzzHIsrPEOqrjJ2sUb9FERfzuBabQ=="], "twoslash": ["twoslash@0.3.6", "", { "dependencies": { "@typescript/vfs": "^1.6.2", "twoslash-protocol": "0.3.6" }, "peerDependencies": { "typescript": "^5.5.0" } }, "sha512-VuI5OKl+MaUO9UIW3rXKoPgHI3X40ZgB/j12VY6h98Ae1mCBihjPvhOPeJWlxCYcmSbmeZt5ZKkK0dsVtp+6pA=="], @@ -3072,8 +3134,6 @@ "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@babel/helper-compilation-targets/browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="], - "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@civ7/studio-server/effect": ["effect@3.21.3", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-RqwU7WnJ6CqYhyjpOVJA5vh1Sgkn6eVECO6mnD0EjlbWcC2M3LJaPglXXr13Rdo/Y+B+wTEPzGRYFNL2xKxNeQ=="], @@ -3224,6 +3284,20 @@ "@stoplight/yaml/@stoplight/types": ["@stoplight/types@14.1.1", "", { "dependencies": { "@types/json-schema": "^7.0.4", "utility-types": "^3.10.0" } }, "sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g=="], + "@tailwindcss/node/jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], "aggregate-error/clean-stack": ["clean-stack@4.2.0", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg=="], @@ -3450,12 +3524,6 @@ "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], - "tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - - "tailwindcss/postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - - "tailwindcss/postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], - "tar/minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="], "tar/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], @@ -3492,14 +3560,6 @@ "@aws-sdk/signature-v4-multi-region/@aws-sdk/middleware-sdk-s3/@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.972.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-RM5Mmo/KJ593iMSrALlHEOcc9YOIyOsDmS5x2NLOMdEmzv1o00fcpAkCQ02IGu1eFneBFT7uX0Mpag0HI+Cz2g=="], - "@babel/helper-compilation-targets/browserslist/baseline-browser-mapping": ["baseline-browser-mapping@2.9.18", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA=="], - - "@babel/helper-compilation-targets/browserslist/caniuse-lite": ["caniuse-lite@1.0.30001766", "", {}, "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA=="], - - "@babel/helper-compilation-targets/browserslist/electron-to-chromium": ["electron-to-chromium@1.5.278", "", {}, "sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw=="], - - "@babel/helper-compilation-targets/browserslist/node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], - "@inquirer/confirm/@inquirer/core/@inquirer/type": ["@inquirer/type@2.0.0", "", { "dependencies": { "mute-stream": "^1.0.0" } }, "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag=="], "@inquirer/confirm/@inquirer/core/mute-stream": ["mute-stream@1.0.0", "", {}, "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA=="], @@ -3730,12 +3790,6 @@ "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - - "tailwindcss/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "update-notifier/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "vite/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], @@ -3834,8 +3888,6 @@ "package-json/got/cacheable-request/responselike": ["responselike@1.0.2", "", { "dependencies": { "lowercase-keys": "^1.0.0" } }, "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ=="], - "tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "vitest/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], "vitest/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], diff --git a/openspec/changes/mapgen-studio-design-system/tasks.md b/openspec/changes/mapgen-studio-design-system/tasks.md index 5d0083dede..7828e0515c 100644 --- a/openspec/changes/mapgen-studio-design-system/tasks.md +++ b/openspec/changes/mapgen-studio-design-system/tasks.md @@ -1,42 +1,44 @@ ## 1. Tailwind v4 migration -- [ ] 1.1 Add `tailwindcss@4`, `@tailwindcss/vite`, `tw-animate-css`; remove +- [x] 1.1 Add `tailwindcss@4`, `@tailwindcss/vite`, `tw-animate-css`; remove `autoprefixer`. Add the `@tailwindcss/vite` plugin to `vite.config.ts` (do not touch the existing `/api` middleware). -- [ ] 1.2 Delete `tailwind.config.js` and `postcss.config.js` (CSS-first v4); fold +- [x] 1.2 Delete `tailwind.config.js` and `postcss.config.js` (CSS-first v4); fold the still-live bits (font families, `pulse-subtle`) into CSS. -- [ ] 1.3 Rewrite `src/index.css` head to `@import "tailwindcss"` + +- [x] 1.3 Rewrite `src/index.css` head to `@import "tailwindcss"` + `@import "tw-animate-css"` + `@custom-variant dark`. ## 2. Token system -- [ ] 2.1 Define the cartographer's-instrument HSL tokens in `:root` (light) and +- [x] 2.1 Define the cartographer's-instrument HSL tokens in `:root` (light) and `.dark` (dark) per `apps/mapgen-studio/.interface-design/system.md`: background/ foreground, card/popover, muted, primary (elevated steel slate), primary-foreground, border/input, ring (contour luminance), destructive, radius. -- [ ] 2.2 Add the felt **substrate-elevation** tier scale (page→panel→nested→ +- [x] 2.2 Add the felt **substrate-elevation** tier scale (page→panel→nested→ floating) and a **border progression** (subtle/default/strong) — the primary craft lever; widen the lightness steps so the squint test shows hierarchy. -- [ ] 2.3 Map tokens through `@theme inline` to Tailwind color/radius utilities and +- [x] 2.3 Map tokens through `@theme inline` to Tailwind color/radius utilities and shadcn aliases. Preserve the legacy `--color-*` vars (focus/scrollbar/select still reference them) by re-pointing them at the new tokens. ## 3. shadcn scaffolding -- [ ] 3.1 Add `components.json` (New York, CSS vars, base color slate, path +- [x] 3.1 Add `components.json` (New York, CSS vars, base color slate, path aliases) and `src/lib/utils.ts` exporting `cn` (clsx + tailwind-merge). ## 4. Theming repair + craft foundation -- [ ] 4.1 Add a no-flash `.dark` bootstrap (inline head script, default dark, +- [x] 4.1 Add a no-flash `.dark` bootstrap (inline head script, default dark, localStorage-backed) and make `.dark` authoritative; keep a `prefers-color-scheme` fallback for first paint during coexistence. -- [ ] 4.2 Replace the global `:focus-visible` outline with the contour-ring focus +- [x] 4.2 Replace the global `:focus-visible` outline with the contour-ring focus signature (ring token, luminance step). Self-host Inter + JetBrains Mono (remove the render-blocking Google Fonts `@import`). ## 5. Verify -- [ ] 5.1 `tsc --noEmit` clean; `vite build` succeeds; worker-bundle check passes. -- [ ] 5.2 App renders in dark and in light (via `.dark` toggle) with no regression - to existing hex-styled components; capture a screenshot diff of the live app. +- [x] 5.1 `tsc --noEmit` clean; `vite build` succeeds; worker-bundle check passes. +- [x] 5.2 App renders in dark (live screenshot, no console errors) with no + regression to existing hex-styled components; tokens ready for component + adoption in later slices. (Light-theme readback deferred to the first + token-consuming component slice, where `.dark` toggling becomes observable.) From 40db11b7cf24ed8467a6b54066aa2ea0fba23548 Mon Sep 17 00:00:00 2001 From: Matei Date: Mon, 8 Jun 2026 23:46:22 -0400 Subject: [PATCH 3/3] docs(mapgen-studio): mark P1 design-system foundation complete in goal ledger Co-Authored-By: Claude Opus 4.8 --- docs/projects/mapgen-studio-redesign/00-GOAL.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/projects/mapgen-studio-redesign/00-GOAL.md b/docs/projects/mapgen-studio-redesign/00-GOAL.md index 5325d3a1ff..fcc61306e0 100644 --- a/docs/projects/mapgen-studio-redesign/00-GOAL.md +++ b/docs/projects/mapgen-studio-redesign/00-GOAL.md @@ -188,7 +188,10 @@ Phase order per [`FRAME.md`](FRAME.md) §7 (design-system-first): - [x] Frame, isolate, research, audit, target architecture, slice plan - [x] Re-frame + un-pause + normative FRAME.md + goal reset (2026-06-08) -- [ ] **P1 Design system** (`design:*` chain) — FIRST DOMINO, in progress next +- [x] **P1 Design system foundation** — DONE (slice `design/ds-foundation`: + `971627971` spec + `4274e60f4` impl). Tailwind v4 + shadcn + tokens + `.dark` + theming + self-hosted fonts. Verified: tsc + prod build + worker bundle + live + render, no console errors, non-breaking. OpenSpec `mapgen-studio-design-system`. - [ ] P2 Client data layer (oRPC-native TanStack Query + Zustand, behind seam) - [ ] P3 Component decomposition (un-god App.tsx onto the design system) - [ ] P4 Primitives→shadcn + new components + rjsf re-skin + craft