Skip to content

Commit 82ff861

Browse files
committed
Update .gitignore, enhance CLAUDE.md documentation, and refactor useOpenLayersTimelineLayer hook
- Added .github/ to .gitignore to exclude GitHub-related files from version control. - Expanded CLAUDE.md with design context sections for better guidance on project structure and design principles. - Refactored useOpenLayersTimelineLayer hook to improve tooltip management and session handling, replacing state with refs for better performance and cleanup.
1 parent aab29e6 commit 82ff861

8 files changed

Lines changed: 472 additions & 71 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,6 @@ coverage/
4949

5050
.agents/
5151

52-
.deepsec/
52+
.deepsec/
53+
54+
.github/

.impeccable/design.json

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
{
2+
"schemaVersion": 2,
3+
"generatedAt": "2026-06-02T00:00:00.000Z",
4+
"title": "Design System: Rybbit",
5+
"extensions": {
6+
"colorMeta": {
7+
"background": { "role": "neutral", "displayName": "Canvas", "canonical": "hsl(0 0% 8%)", "tonalRamp": ["#0a0a0a", "#131313", "#1b1b1b", "#242424", "#303030", "#4a4a4a", "#808080", "#f7f7f7"] },
8+
"surface": { "role": "neutral", "displayName": "Surface", "canonical": "hsl(0 0% 10.5%)", "tonalRamp": ["#0a0a0a", "#131313", "#1b1b1b", "#242424", "#303030", "#4a4a4a", "#999999", "#f2f2f2"] },
9+
"foreground": { "role": "neutral", "displayName": "Primary Text", "canonical": "hsl(0 0% 98%)", "tonalRamp": ["#131313", "#303030", "#4a4a4a", "#808080", "#999999", "#cccccc", "#e6e6e6", "#fafafa"] },
10+
"muted-foreground": { "role": "neutral", "displayName": "Muted Text", "canonical": "hsl(0 0% 60%)", "tonalRamp": ["#303030", "#4a4a4a", "#5e5e5e", "#737373", "#808080", "#999999", "#b3b3b3", "#cccccc"] },
11+
"accent": { "role": "primary", "displayName": "Emerald Signal", "canonical": "hsl(160.1 84.1% 39.4%)", "tonalRamp": ["#022c22", "#064e3b", "#065f46", "#047857", "#059669", "#10b981", "#34d399", "#6ee7b7"] },
12+
"dataviz": { "role": "secondary", "displayName": "Periwinkle Data Line", "canonical": "hsl(230 100% 85%)", "tonalRamp": ["#1e2a66", "#33408c", "#4d5cb3", "#6677d9", "#8090ff", "#99aaff", "#b3bfff", "#ccd4ff"] },
13+
"destructive": { "role": "tertiary", "displayName": "Destructive Red", "canonical": "hsl(0 84.2% 60.2%)", "tonalRamp": ["#450a0a", "#7f1d1d", "#991b1b", "#b91c1c", "#dc2626", "#ef4444", "#f87171", "#fca5a5"] },
14+
"warning": { "role": "tertiary", "displayName": "Warning Yellow", "canonical": "hsl(45.4 93.4% 47.5%)", "tonalRamp": ["#422006", "#713f12", "#854d0e", "#a16207", "#ca8a04", "#eab308", "#facc15", "#fde047"] },
15+
"info": { "role": "tertiary", "displayName": "Info Blue", "canonical": "hsl(217.2 91.2% 59.8%)", "tonalRamp": ["#172554", "#1e3a8a", "#1e40af", "#1d4ed8", "#2563eb", "#3b82f6", "#60a5fa", "#93c5fd"] }
16+
},
17+
"typographyMeta": {
18+
"headline": { "displayName": "Headline", "purpose": "Large stat values and primary metric numbers; tabular figures." },
19+
"title": { "displayName": "Title", "purpose": "Card titles, section and panel headings." },
20+
"body": { "displayName": "Body", "purpose": "Dominant size: table cells, descriptions, most UI text." },
21+
"label": { "displayName": "Label", "purpose": "Badges, chips, captions, axis labels, metadata." }
22+
},
23+
"shadows": [],
24+
"motion": [
25+
{ "name": "transition-colors", "value": "150ms ease", "purpose": "Default for button/badge/input color and border state changes." },
26+
{ "name": "transition-all-card", "value": "300ms ease", "purpose": "Card hover border-color shift; the only depth motion." },
27+
{ "name": "accordion", "value": "0.2s ease-out", "purpose": "Radix accordion open/close height." }
28+
],
29+
"breakpoints": [
30+
{ "name": "sm", "value": "640px" },
31+
{ "name": "md", "value": "768px" },
32+
{ "name": "lg", "value": "1024px" },
33+
{ "name": "xl", "value": "1280px" },
34+
{ "name": "2xl", "value": "1400px" }
35+
]
36+
},
37+
"components": [
38+
{
39+
"name": "Accent Button",
40+
"kind": "button",
41+
"refersTo": "button-accent",
42+
"description": "Primary call to action / success. The one emerald button per view.",
43+
"html": "<button class=\"ds-btn-accent\">Save changes</button>",
44+
"css": ".ds-btn-accent { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 36px; padding: 8px 12px; font-family: Inter, system-ui, sans-serif; font-size: 14px; font-weight: 500; color: #fafafa; background: #059669; border: 1px solid #047857; border-radius: 4.8px; cursor: pointer; transition: background-color 150ms ease, border-color 150ms ease; } .ds-btn-accent:hover { background: rgba(5,150,105,0.9); } .ds-btn-accent:focus-visible { outline: none; box-shadow: 0 0 0 1px #d4d4d4; }"
45+
},
46+
{
47+
"name": "Default Button",
48+
"kind": "button",
49+
"refersTo": "button-default",
50+
"description": "Neutral workhorse button: raised surface with a hairline border.",
51+
"html": "<button class=\"ds-btn-default\">Add filter</button>",
52+
"css": ".ds-btn-default { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 36px; padding: 8px 12px; font-family: Inter, system-ui, sans-serif; font-size: 14px; font-weight: 500; color: #fafafa; background: #242424; border: 1px solid #3d3d3d; border-radius: 4.8px; cursor: pointer; transition: background-color 150ms ease, border-color 150ms ease; } .ds-btn-default:hover { background: rgba(48,48,48,0.9); border-color: #4a4a4a; } .ds-btn-default:focus-visible { outline: none; box-shadow: 0 0 0 1px #d4d4d4; }"
53+
},
54+
{
55+
"name": "Outline Button",
56+
"kind": "button",
57+
"refersTo": "button-outline",
58+
"description": "Secondary/tertiary action: transparent with muted text.",
59+
"html": "<button class=\"ds-btn-outline\">Cancel</button>",
60+
"css": ".ds-btn-outline { display: inline-flex; align-items: center; justify-content: center; gap: 8px; height: 36px; padding: 8px 12px; font-family: Inter, system-ui, sans-serif; font-size: 14px; font-weight: 500; color: #d4d4d4; background: transparent; border: 1px solid #4a4a4a; border-radius: 4.8px; cursor: pointer; transition: background-color 150ms ease, color 150ms ease, border-color 150ms ease; } .ds-btn-outline:hover { background: #1b1b1b; color: #fafafa; border-color: #5e5e5e; } .ds-btn-outline:focus-visible { outline: none; box-shadow: 0 0 0 1px #d4d4d4; }"
61+
},
62+
{
63+
"name": "Card",
64+
"kind": "card",
65+
"refersTo": "card",
66+
"description": "Flat surface one step off the canvas, hairline border, no resting shadow.",
67+
"html": "<div class=\"ds-card\"><div class=\"ds-card-title\">Unique visitors</div><div class=\"ds-card-metric\">12,480</div><div class=\"ds-card-desc\">Last 7 days</div></div>",
68+
"css": ".ds-card { background: #1b1b1b; color: #fafafa; border: 1px solid #242424; border-radius: 4.8px; padding: 16px; overflow: hidden; transition: border-color 300ms ease; } .ds-card:hover { border-color: #303030; } .ds-card-title { font-family: Inter, system-ui, sans-serif; font-size: 16px; font-weight: 600; line-height: 1; letter-spacing: -0.01em; } .ds-card-metric { font-family: Inter, system-ui, sans-serif; font-size: 30px; font-weight: 600; line-height: 1.1; letter-spacing: -0.01em; font-variant-numeric: tabular-nums; margin-top: 8px; } .ds-card-desc { font-family: Inter, system-ui, sans-serif; font-size: 14px; color: #999999; margin-top: 6px; }"
69+
},
70+
{
71+
"name": "Search Input",
72+
"kind": "input",
73+
"refersTo": "input",
74+
"description": "Transparent field, hairline border, leading search icon, neutral focus ring.",
75+
"html": "<div class=\"ds-input-wrap\"><svg class=\"ds-input-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"8\"></circle><path d=\"m21 21-4.3-4.3\"></path></svg><input class=\"ds-input\" type=\"text\" placeholder=\"Search pages\" /></div>",
76+
"css": ".ds-input-wrap { position: relative; display: inline-block; } .ds-input-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: #999999; pointer-events: none; } .ds-input { height: 36px; width: 100%; padding: 4px 12px 4px 36px; font-family: Inter, system-ui, sans-serif; font-size: 14px; color: #fafafa; background: transparent; border: 1px solid #303030; border-radius: 4.8px; transition: border-color 150ms ease; } .ds-input::placeholder { color: #999999; } .ds-input:focus-visible { outline: none; box-shadow: 0 0 0 1px #d4d4d4; }"
77+
},
78+
{
79+
"name": "Success Badge",
80+
"kind": "chip",
81+
"refersTo": "badge-success",
82+
"description": "Semantic tint badge: 20% hue fill with the lighter text shade, transparent border.",
83+
"html": "<span class=\"ds-badge-success\">Verified</span>",
84+
"css": ".ds-badge-success { display: inline-flex; align-items: center; padding: 2px 6px; font-family: Inter, system-ui, sans-serif; font-size: 12px; font-weight: 500; color: #34d399; background: rgba(16,185,129,0.20); border: 1px solid transparent; border-radius: 2.8px; }"
85+
},
86+
{
87+
"name": "Neutral Badge",
88+
"kind": "chip",
89+
"refersTo": "badge-default",
90+
"description": "Default grayscale chip for tags and metadata.",
91+
"html": "<span class=\"ds-badge\">Direct</span>",
92+
"css": ".ds-badge { display: inline-flex; align-items: center; padding: 2px 6px; font-family: Inter, system-ui, sans-serif; font-size: 12px; font-weight: 500; color: #e6e6e6; background: #242424; border: 1px solid #242424; border-radius: 2.8px; transition: background-color 150ms ease; } .ds-badge:hover { background: #303030; }"
93+
}
94+
],
95+
"narrative": {
96+
"northStar": "The Instrument Panel",
97+
"overview": "Rybbit is a precise, legible cockpit for understanding a website. Every surface is flat; depth comes from tonal layering of a pure-grayscale ramp, not from shadows. The canvas recedes (near-black in the default dark theme), panels lift one notch, interactive chrome lifts one more, and hairline 1px borders draw the seams. Onto that calm gray instrument, two signals are painted with intent: a single emerald for action and success, and a single periwinkle for the data lines themselves. The data is the instrument; the chrome stays out of the way. The personality is friendly, precise, and trustworthy: approachable without being toy-like. Warmth lives at the edges, never on top of the numbers. Density is a feature here: this is a tool people open to answer a specific question, and the layout should put that answer in front of them in seconds with depth one layer down.",
98+
"keyCharacteristics": [
99+
"Dark-mode-default; a full light theme mirrors every token.",
100+
"Flat by default: depth via a 21-step grayscale ramp + 1px borders, never shadows.",
101+
"One accent (emerald) for action/success, one data hue (periwinkle) for charts.",
102+
"Tight geometry: 4.8px max radius, Inter throughout, compact 36px controls.",
103+
"Density with legibility: dense data, but body text stays ≥4.5:1 contrast."
104+
],
105+
"rules": [
106+
{ "name": "The Signal-Not-Decoration Rule", "body": "Emerald marks primary actions, success, and current selection only. It never tints a surface, never fills a background, never decorates. If emerald is on screen and nothing actionable or successful is being communicated, remove it.", "section": "colors" },
107+
{ "name": "The Two-Voice Rule", "body": "The interface speaks in grayscale. Exactly two chromatic voices are allowed on top: emerald for action, periwinkle for data. State colors (red/yellow/blue) are whispers that appear only when state demands them.", "section": "colors" },
108+
{ "name": "The Flat Rule", "body": "No box-shadow for resting elevation. Depth is the grayscale ramp plus 1px borders. If two surfaces need separating, change the neutral step or add a hairline border, not a shadow.", "section": "elevation" },
109+
{ "name": "The Tonal-Depth Rule", "body": "Separation is achieved by moving one step on the neutral ramp or adding a 1px border, never by stacking shadows. Floating overlays (dropdowns, dialogs, popovers) may use minimal platform elevation, but in-page surfaces stay flat.", "section": "elevation" },
110+
{ "name": "The One-Family Rule", "body": "Inter does every job: headings, body, labels, data. Do not introduce a second UI typeface. Hierarchy comes from weight (400/500/600) and size, never from a new face.", "section": "typography" },
111+
{ "name": "The No-Shrinking-Headline Rule", "body": "Type uses a fixed rem scale, not fluid clamp(). Users view at a consistent DPI inside an app shell; a heading that shrinks in a sidebar looks worse, not better.", "section": "typography" }
112+
],
113+
"dos": [
114+
"Do keep surfaces flat: convey depth with the neutral ramp (#141414 -> #1b1b1b -> #242424) and 1px borders.",
115+
"Do reserve emerald for action, success, and current selection (The Signal-Not-Decoration Rule).",
116+
"Do keep data viz in periwinkle (--dataviz) and the broader ramp; never use the data hue on chrome.",
117+
"Do use Inter at fixed rem sizes with weight contrast (400/500/600) for all hierarchy.",
118+
"Do keep radii tight (<=4.8px) and controls compact (36px default height).",
119+
"Do keep a visible focus-visible ring on every interactive element, and honor prefers-reduced-motion.",
120+
"Do mirror every token across dark and light themes; dark is the default."
121+
],
122+
"donts": [
123+
"Don't ship the generic SaaS template: no purple gradients, no hero-metric template, no endless identical icon+heading+text card grids.",
124+
"Don't let the frog or friendly tone tip into over-playful / toy-like territory that undermines trust in the numbers.",
125+
"Don't recreate the cluttered GA4 maze or cold enterprise-BI heaviness: surface the likely answer first, depth one layer down.",
126+
"Don't add resting box-shadow for elevation (The Flat Rule).",
127+
"Don't use border-left/border-right > 1px as a colored accent stripe.",
128+
"Don't use gradient text (background-clip: text) or decorative glassmorphism.",
129+
"Don't introduce a second UI typeface or fluid clamp() headings.",
130+
"Don't tint surfaces or backgrounds with emerald, or paint chrome in the periwinkle data hue."
131+
]
132+
}
133+
}

.impeccable/live/config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"files": ["client/src/app/layout.tsx"],
3+
"insertBefore": "</body>",
4+
"commentSyntax": "jsx",
5+
"cspChecked": true
6+
}

CLAUDE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
2222
- File organization: Related functionality in same directory
2323
- Dark mode is default theme
2424
- Never run any database migration scripts
25+
26+
## Design Context
27+
28+
- `PRODUCT.md` (repo root) — strategic design context: register (`product`), users, purpose, brand personality, anti-references, and design principles. Read it before frontend/design work.
29+
- `DESIGN.md` (repo root) — visual system: color tokens, typography, components, layout. The source of truth for visual decisions.
30+
- The `/impeccable` skill reads both files before any design task.

0 commit comments

Comments
 (0)