diff --git a/docs/ui/theming.md b/docs/ui/theming.md index c5ae405d..5db7464b 100644 --- a/docs/ui/theming.md +++ b/docs/ui/theming.md @@ -55,6 +55,8 @@ editor frame — is written in the semantic tokens of `src/styles/theme.css`. Tw |------|-----------------------------------------------------| | Surface | `canvas` · `sunken` · `surface` · `raised` · `overlay` (plus `panel`, the translucent card ground) | | Text | `fg-bright` · `fg` · `fg-secondary` · `fg-tertiary` · `fg-muted` · `fg-subtle` · `fg-faint` | +| Accent, state | `brand` · `warning` · `success` · `danger`, each with `-bright`, `-tint`, `-solid`, `-solid-hover` (plus `brand-solid-active`, the one filled ground that hovers darker) | +| Accent, identity | `hue-`, one per hue the app uses, some with `-alt` (a second step) and `-tint` (its wash); `hue-teal` and `hue-purple` also carry `-solid` / `-solid-hover` for the two filled controls painted in a panel's own hue | Alongside them: `hairline` / `hairline-strong` for structural rules, `edge` / `edge-hover` for the border of a control the user is meant to see, and `fill-subtle` / `fill` / `fill-strong` @@ -67,6 +69,34 @@ literals the components carried before the layer existed, so **moving a componen must be a no-op in dark** — any visible dark-mode change is a bug unless it is deliberate and called out. +### State or identity + +The two accent families answer different questions, and picking the wrong one is the mistake +that costs something later. Note the state family is `brand`, not `accent`: shadcn already owns +`--accent` — its neutral hover ground, `#f5f5f5` — and `globals.css` maps `--color-accent` to it +*after* importing this layer, so a studio token of that name loses the cascade silently and paints +near-white text on a white page. + +- **State** — the colour tracks a changing condition. A run failed, a row is selected, a + statement is risky. Four roles: `brand`, `warning`, `success`, `danger`. `-bright` is the + emphasised step and inverts exactly like `fg-bright` — brighter than its base in dark, darker + in light, both meaning *further from the ground*. +- **Identity** — the colour is a fixed label for a thing. Which engine, which bottom panel, + added versus removed, primary key versus foreign key, number versus boolean. `hue-`, and + `hue--alt` where two identities share a hue: there are more engines than there are hues, + and `db-ui-config`'s own test asserts every engine colour differs. + +Folding an identity into a state role repaints seventeen engines in four colours. Folding a state +into an identity hue means the next person to change what "error" looks like has to find every +red in the codebase. The identity set is **selected per mode**, the way `lib/charts/palette.ts` +selects rather than flipping a ramp — the two modes run out of room in different places, so a +ramp flip produces collisions in one of them. + +`-tint` is the wash a role is painted over (`bg-brand-tint/15`) and is the same value in both +palettes on purpose: a wash is alpha over whatever is behind it, so it already adapts. `-solid` +is a filled control's ground, mode-independent for the same reason — a button's label sits on +the button, not on the page. + ### Surfaces that cannot read CSS Monaco, Recharts and the `@xyflow` ER diagram paint their own canvas from a JS palette, so they @@ -308,15 +338,17 @@ shadcn/ui buttons use theme variables automatically: ### Step 1: Define Variables +Both palettes, always. A token declared in one resolves to nothing in the other, which is invalid +at computed-value time — the ground falls to transparent, the hairline to `currentColor`, and only +in the theme nobody happened to be looking at. + ```css :root { - --warning: #f59e0b; - --warning-foreground: #ffffff; + --studio-hue-lime: #3f6212; } .dark { - --warning: #d97706; - --warning-foreground: #ffffff; + --studio-hue-lime: #9ae600; } ``` @@ -324,19 +356,41 @@ shadcn/ui buttons use theme variables automatically: ```css @theme inline { - --color-warning: var(--warning); - --color-warning-foreground: var(--warning-foreground); + --color-hue-lime: var(--studio-hue-lime); } ``` +`inline` is required: a plain `@theme` resolves the value at build time and freezes whichever +palette was in scope. + ### Step 3: Use in Components ```jsx -
- Warning message -
+ ``` +### Step 4: Let the suite check it + +Do not verify a colour by eye, and do not write the ratio into a comment. `tests/unit/theme-accent-contrast.test.ts` +measures every accent token on every ground it can land on, in both palettes, and +`tests/unit/theme-token-usage.test.ts` fails on a colour literal in `src` and on a token nothing +reaches. The bars a new token has to clear: + +- **4.5:1** on the five studio grounds, on a wash of its own hue at every alpha the code + actually paints (scanned out of `src`, currently up to `/25`), and on the brand tile — WCAG AA + for text, in both palettes. +- **No worse in light than in dark** if the token is ever used with a foreground opacity + (`text-warning/80`). An opacity modifier composites before anyone reads it, so a token that + clears AA opaque can fall under it faded; the suite measures those separately and pins the + ones that do not clear. +- **Separation** no tighter than the shipped dark set's own minimum, if it joins the identity + palette. The bar is measured, not chosen, so it moves if dark is ever retuned. +- **A call site.** A token nobody uses is a value nobody has checked. + +The helper the tests measure with is `tests/helpers/contrast.ts`. It reads the Tailwind palette out +of `node_modules` rather than transcribing it, so a Tailwind upgrade that restyles `blue-400` +reports itself as the dark-mode change it is. + ## Troubleshooting ### Colors Not Applying @@ -413,4 +467,4 @@ shadcn/ui buttons use theme variables automatically: --- -*Last updated: June 2026* +*Last updated: September 2026* diff --git a/src/app/admin/error.tsx b/src/app/admin/error.tsx index 40bc01a1..c35ad1d7 100644 --- a/src/app/admin/error.tsx +++ b/src/app/admin/error.tsx @@ -18,7 +18,7 @@ export default function AdminError({ error, reset }: { error: Error & { digest?:
diff --git a/src/app/error.tsx b/src/app/error.tsx index 712eb3d1..d737f84c 100644 --- a/src/app/error.tsx +++ b/src/app/error.tsx @@ -19,7 +19,7 @@ export default function ErrorPage({ error, reset }: { error: Error & { digest?:
diff --git a/src/app/login/login-form.tsx b/src/app/login/login-form.tsx index 488085d7..f2a99c4c 100644 --- a/src/app/login/login-form.tsx +++ b/src/app/login/login-form.tsx @@ -93,8 +93,8 @@ function LoginFormInner({ authProvider }: { authProvider: string }) { /> {/* Ambient glow orbs — blue accent family */} -
-
+
+
{/* Right edge separator */}
@@ -109,9 +109,9 @@ function LoginFormInner({ authProvider }: { authProvider: string }) { className="flex items-center gap-3 group w-fit" >
- +
- + LibreDB Studio @@ -186,13 +186,13 @@ function LoginFormInner({ authProvider }: { authProvider: string }) { className="flex flex-col items-center gap-4 lg:hidden group" >
-
-
- +
+
+
-

+

LibreDB Studio

Open-source SQL IDE for cloud-native teams

diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 80c703c9..f8f998b8 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -10,7 +10,7 @@ export default function NotFound() {
Go to Studio diff --git a/src/components/CodeGenerator.tsx b/src/components/CodeGenerator.tsx index 5555cbd0..67b7553d 100644 --- a/src/components/CodeGenerator.tsx +++ b/src/components/CodeGenerator.tsx @@ -266,7 +266,7 @@ export function CodeGenerator({ isOpen, onClose, tableName, tableSchema, databas
- + Code Generator {tableName} {databaseType && {databaseType}} @@ -296,7 +296,7 @@ export function CodeGenerator({ isOpen, onClose, tableName, tableSchema, databas }} className={cn( "w-full text-left px-3 py-1.5 text-xs hover:bg-fill transition-colors", - language === lang.id ? "text-purple-400" : "text-fg-tertiary", + language === lang.id ? "text-hue-purple" : "text-fg-tertiary", )} > {lang.label} diff --git a/src/components/CommandPalette.tsx b/src/components/CommandPalette.tsx index 0886a6c4..be6c4137 100644 --- a/src/components/CommandPalette.tsx +++ b/src/components/CommandPalette.tsx @@ -113,7 +113,7 @@ export function CommandPalette({ {/* Quick Actions */} runAction(onExecuteQuery)}> - + Run Query Ctrl+Enter @@ -132,30 +132,30 @@ export function CommandPalette({ carries the editor's statement in with it (review of #331 T3). */ runAction(onAskAgent)}> - + Ask the agent about this query )} runAction(onAddConnection)}> - + New Connection runAction(onNavigateHealth)}> - + Health Dashboard runAction(onNavigateMonitoring)}> - + Monitoring {activeConnection && ( runAction(onShowDiagram)}> - + Schema Diagram (ERD) )} runAction(onLogout)}> - + Logout @@ -170,7 +170,7 @@ export function CommandPalette({ {conn.name} {activeConnection?.id === conn.id && ( - Active + Active )} ); @@ -199,7 +199,7 @@ export function CommandPalette({ {savedQueries.map((sq: SavedQuery) => ( runAction(() => onLoadSavedQuery(sq.query))}> - + {sq.name} {sq.query.substring(0, 40)}... diff --git a/src/components/ConnectionModal.tsx b/src/components/ConnectionModal.tsx index b334994a..16c98128 100644 --- a/src/components/ConnectionModal.tsx +++ b/src/components/ConnectionModal.tsx @@ -195,11 +195,11 @@ export function ConnectionModal({ const formContent = ( <> {/* Progress bar — fixed top */} -
+
@@ -207,8 +207,8 @@ export function ConnectionModal({
-
- +
+

{isEditMode ? "Edit Connection" : "New Connection"} @@ -223,7 +223,7 @@ export function ConnectionModal({ {!isEditMode && ( @@ -281,7 +281,7 @@ export function ConnectionModal({ value={name} onChange={(e) => setName(e.target.value)} placeholder="My Database" - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs" />

@@ -322,7 +322,7 @@ export function ConnectionModal({ className={cn( "flex flex-col items-center justify-center p-3 md:p-4 rounded-xl border transition-all duration-200 gap-2 group", type === db.value - ? "bg-blue-600/10 border-blue-500/50 shadow-[0_0_20px_rgba(59,130,246,0.1)]" + ? "bg-brand-solid/10 border-brand-tint/50 shadow-[0_0_20px_rgba(59,130,246,0.1)]" : "bg-panel border-hairline hover:border-hairline-strong hover:bg-raised", isEditMode && type !== db.value && "opacity-30 cursor-not-allowed", )} @@ -353,7 +353,7 @@ export function ConnectionModal({ className={cn( "flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-md text-xs font-medium transition-all", mongoConnectionMode === "host" - ? "bg-blue-600/20 text-blue-400 border border-blue-500/30" + ? "bg-brand-solid/20 text-brand border border-brand-tint/30" : "text-fg-muted hover:text-fg-secondary", )} > @@ -365,7 +365,7 @@ export function ConnectionModal({ className={cn( "flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-md text-xs font-medium transition-all", mongoConnectionMode === "connectionString" - ? "bg-blue-600/20 text-blue-400 border border-blue-500/30" + ? "bg-brand-solid/20 text-brand border border-brand-tint/30" : "text-fg-muted hover:text-fg-secondary", )} > @@ -389,7 +389,7 @@ export function ConnectionModal({ value={connectionString} onChange={(e) => setConnectionString(e.target.value)} placeholder={connectionUriPlaceholder} - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs font-mono" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs font-mono" />
@@ -404,7 +404,7 @@ export function ConnectionModal({ value={database} onChange={(e) => setDatabase(e.target.value)} placeholder="Extracted from URI if not provided" - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs font-mono" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs font-mono" />
@@ -421,7 +421,7 @@ export function ConnectionModal({ value={database} onChange={(e) => setDatabase(e.target.value)} placeholder="/path/to/database file" - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs font-mono" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs font-mono" />
) : ( @@ -440,14 +440,14 @@ export function ConnectionModal({ onChange={(e) => setHost(e.target.value)} placeholder="localhost" autoComplete="off" - className="md:col-span-3 h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs" + className="md:col-span-3 h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs" /> setPort(e.target.value)} autoComplete="off" - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs font-mono" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs font-mono" />
@@ -472,7 +472,7 @@ export function ConnectionModal({ onChange={(e) => setUser(e.target.value)} placeholder="user" autoComplete="off" - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs" />
)} @@ -492,7 +492,7 @@ export function ConnectionModal({ // Server credential, not the user's own login: "new-password" is the only // value Chrome honours to keep saved site passwords out of the field. autoComplete="new-password" - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs" /> {/* Measured on Trino 476 with authentication DISABLED: a request @@ -537,7 +537,7 @@ export function ConnectionModal({ value={database} onChange={(e) => setDatabase(e.target.value)} placeholder={databaseFieldPlaceholder} - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs font-mono" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs font-mono" /> {isTrino && (

@@ -573,7 +573,7 @@ export function ConnectionModal({ value={authSource} onChange={(e) => setAuthSource(e.target.value)} placeholder="admin" - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs font-mono" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs font-mono" />

The database the user was created in, usually admin. Leave empty when the credentials live in @@ -602,7 +602,7 @@ export function ConnectionModal({ value={localDataCenter} onChange={(e) => setLocalDataCenter(e.target.value)} placeholder="datacenter1" - className="h-10 bg-panel border-hairline focus:border-blue-500/50 transition-all text-xs font-mono" + className="h-10 bg-panel border-hairline focus:border-brand-tint/50 transition-all text-xs font-mono" />

Required: the Cassandra driver refuses to connect without it. A stock single-node install @@ -623,10 +623,10 @@ export function ConnectionModal({ onClick={() => setShowAdvanced(!showAdvanced)} className="flex items-center gap-2 w-full px-3 py-2 rounded-lg border border-hairline hover:border-hairline-strong bg-panel text-xs font-medium text-fg-tertiary hover:text-fg transition-all" > - + Advanced {(serviceName || instanceName) && ( - + SET )} @@ -640,7 +640,7 @@ export function ConnectionModal({ exit={{ height: 0, opacity: 0 }} className="overflow-hidden" > -

+
{type === "oracle" && (
@@ -648,7 +648,7 @@ export function ConnectionModal({ value={serviceName} onChange={(e) => setServiceName(e.target.value)} placeholder="ORCL or XEPDB1" - className="h-9 bg-panel border-hairline focus:border-orange-500/50 text-xs" + className="h-9 bg-panel border-hairline focus:border-hue-orange-tint/50 text-xs" />

If empty, the Database Name field is used as the service name. @@ -662,7 +662,7 @@ export function ConnectionModal({ value={instanceName} onChange={(e) => setInstanceName(e.target.value)} placeholder="SQLEXPRESS" - className="h-9 bg-panel border-hairline focus:border-orange-500/50 text-xs" + className="h-9 bg-panel border-hairline focus:border-hue-orange-tint/50 text-xs" />

For named instances (e.g. SQLEXPRESS). Leave empty for default instance. @@ -685,10 +685,10 @@ export function ConnectionModal({ onClick={() => setShowSSL(!showSSL)} className="flex items-center gap-2 w-full px-3 py-2 rounded-lg border border-hairline hover:border-hairline-strong bg-panel text-xs font-medium text-fg-tertiary hover:text-fg transition-all" > - + SSL / TLS {sslMode !== "disable" && ( - + {sslMode.toUpperCase()} )} @@ -702,7 +702,7 @@ export function ConnectionModal({ exit={{ height: 0, opacity: 0 }} className="overflow-hidden" > -

+
@@ -715,7 +715,7 @@ export function ConnectionModal({ className={cn( "px-2.5 py-1.5 rounded-md text-xs font-mediumr transition-all border", sslMode === mode - ? "border-emerald-500/30 bg-emerald-500/10 text-emerald-400" + ? "border-hue-emerald-tint/30 bg-hue-emerald-tint/10 text-hue-emerald" : "border-transparent text-fg-muted hover:text-fg-secondary hover:bg-fill", )} > @@ -737,7 +737,7 @@ export function ConnectionModal({ onChange={(e) => setCaCert(e.target.value)} placeholder="-----BEGIN CERTIFICATE----- Paste CA cert content here... -----END CERTIFICATE-----" rows={3} - className="w-full rounded-md bg-panel border border-hairline focus:border-emerald-500/50 text-xs font-mono text-fg-secondary p-2 resize-none placeholder:text-fg-subtle" + className="w-full rounded-md bg-panel border border-hairline focus:border-hue-emerald-tint/50 text-xs font-mono text-fg-secondary p-2 resize-none placeholder:text-fg-subtle" />
{(sslMode === "verify-ca" || sslMode === "verify-full") && ( @@ -749,7 +749,7 @@ export function ConnectionModal({ onChange={(e) => setClientCert(e.target.value)} placeholder="-----BEGIN CERTIFICATE----- Optional client cert... -----END CERTIFICATE-----" rows={3} - className="w-full rounded-md bg-panel border border-hairline focus:border-emerald-500/50 text-xs font-mono text-fg-secondary p-2 resize-none placeholder:text-fg-subtle" + className="w-full rounded-md bg-panel border border-hairline focus:border-hue-emerald-tint/50 text-xs font-mono text-fg-secondary p-2 resize-none placeholder:text-fg-subtle" />
@@ -759,7 +759,7 @@ export function ConnectionModal({ onChange={(e) => setClientKey(e.target.value)} placeholder="-----BEGIN PRIVATE KEY----- Optional client key... -----END PRIVATE KEY-----" rows={3} - className="w-full rounded-md bg-panel border border-hairline focus:border-emerald-500/50 text-xs font-mono text-fg-secondary p-2 resize-none placeholder:text-fg-subtle" + className="w-full rounded-md bg-panel border border-hairline focus:border-hue-emerald-tint/50 text-xs font-mono text-fg-secondary p-2 resize-none placeholder:text-fg-subtle" />
@@ -777,10 +777,10 @@ export function ConnectionModal({ onClick={() => setShowSSH(!showSSH)} className="flex items-center gap-2 w-full px-3 py-2 rounded-lg border border-hairline hover:border-hairline-strong bg-panel text-xs font-medium text-fg-tertiary hover:text-fg transition-all" > - + SSH Tunnel {sshEnabled && ( - + ON )} @@ -794,7 +794,7 @@ export function ConnectionModal({ exit={{ height: 0, opacity: 0 }} className="overflow-hidden" > -
+
@@ -823,7 +823,7 @@ export function ConnectionModal({ value={sshPort} onChange={(e) => setSSHPort(e.target.value)} autoComplete="off" - className="h-9 bg-panel border-hairline focus:border-purple-500/50 text-xs font-mono" + className="h-9 bg-panel border-hairline focus:border-hue-purple-tint/50 text-xs font-mono" />
@@ -834,7 +834,7 @@ export function ConnectionModal({ onChange={(e) => setSSHUsername(e.target.value)} placeholder="ubuntu" autoComplete="off" - className="h-9 bg-panel border-hairline focus:border-purple-500/50 text-xs" + className="h-9 bg-panel border-hairline focus:border-hue-purple-tint/50 text-xs" />
@@ -846,7 +846,7 @@ export function ConnectionModal({ className={cn( "flex-1 px-3 py-1.5 rounded-md text-xs font-mediumr transition-all border", sshAuthMethod === "password" - ? "border-purple-500/30 bg-purple-500/10 text-purple-400" + ? "border-hue-purple-tint/30 bg-hue-purple-tint/10 text-hue-purple" : "border-transparent text-fg-muted hover:text-fg-secondary hover:bg-fill", )} > @@ -858,7 +858,7 @@ export function ConnectionModal({ className={cn( "flex-1 px-3 py-1.5 rounded-md text-xs font-mediumr transition-all border", sshAuthMethod === "privateKey" - ? "border-purple-500/30 bg-purple-500/10 text-purple-400" + ? "border-hue-purple-tint/30 bg-hue-purple-tint/10 text-hue-purple" : "border-transparent text-fg-muted hover:text-fg-secondary hover:bg-fill", )} > @@ -875,7 +875,7 @@ export function ConnectionModal({ onChange={(e) => setSSHPassword(e.target.value)} placeholder="••••••••" autoComplete="new-password" - className="h-9 bg-panel border-hairline focus:border-purple-500/50 text-xs" + className="h-9 bg-panel border-hairline focus:border-hue-purple-tint/50 text-xs" />
) : ( @@ -887,7 +887,7 @@ export function ConnectionModal({ onChange={(e) => setSSHPrivateKey(e.target.value)} placeholder="-----BEGIN OPENSSH PRIVATE KEY----- Paste private key here... -----END OPENSSH PRIVATE KEY-----" rows={4} - className="w-full rounded-md bg-panel border border-hairline focus:border-purple-500/50 text-xs font-mono text-fg-secondary p-2 resize-none placeholder:text-fg-subtle" + className="w-full rounded-md bg-panel border border-hairline focus:border-hue-purple-tint/50 text-xs font-mono text-fg-secondary p-2 resize-none placeholder:text-fg-subtle" />
@@ -898,7 +898,7 @@ export function ConnectionModal({ onChange={(e) => setSSHPassphrase(e.target.value)} placeholder="Key passphrase (if encrypted)" autoComplete="new-password" - className="h-9 bg-panel border-hairline focus:border-purple-500/50 text-xs" + className="h-9 bg-panel border-hairline focus:border-hue-purple-tint/50 text-xs" />
@@ -927,10 +927,10 @@ export function ConnectionModal({ className={cn( "flex items-center gap-2 p-3 rounded-lg border text-xs", testResult.tone === "success" - ? "bg-emerald-500/5 border-emerald-500/20 text-emerald-400" + ? "bg-success-tint/5 border-success-tint/20 text-success" : testResult.tone === "warning" - ? "bg-amber-500/5 border-amber-500/20 text-amber-400" - : "bg-red-500/5 border-red-500/20 text-red-400", + ? "bg-warning-tint/5 border-warning-tint/20 text-warning" + : "bg-danger-tint/5 border-danger-tint/20 text-danger", )} > {testResult.tone === "success" ? ( @@ -983,7 +983,7 @@ export function ConnectionModal({ mongoConnectionMode === "connectionString" && !connectionString.trim()) } - className="w-full md:w-auto min-w-0 md:min-w-[140px] bg-blue-600 hover:bg-blue-500 text-white font-medium text-xs h-10 shadow-lg shadow-blue-900/20 group relative overflow-hidden" + className="w-full md:w-auto min-w-0 md:min-w-[140px] bg-brand-solid hover:bg-brand-solid-hover text-white font-medium text-xs h-10 shadow-lg shadow-blue-900/20 group relative overflow-hidden" > {isTesting ? ( diff --git a/src/components/CreateTableModal.tsx b/src/components/CreateTableModal.tsx index e79067c2..76a735f3 100644 --- a/src/components/CreateTableModal.tsx +++ b/src/components/CreateTableModal.tsx @@ -107,8 +107,8 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl
-
- +
+
Create New Table @@ -121,7 +121,7 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl {/* Table Name Section */}
- +
@@ -133,7 +133,7 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl placeholder="e.g. customers, orders, analytics_logs" value={tableName} onChange={(e) => setTableName(e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "_"))} - className="bg-raised border-hairline focus-visible:ring-blue-500/50" + className="bg-raised border-hairline focus-visible:ring-brand-tint/50" />
@@ -142,14 +142,14 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl
- +
@@ -186,7 +186,7 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl ))} {col.isPrimary && ( - + SERIAL (Auto-Inc) )} @@ -205,7 +205,7 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl isNullable: checked ? false : col.isNullable, }) } - className="border-edge data-[state=checked]:bg-yellow-500 data-[state=checked]:border-yellow-500" + className="border-edge data-[state=checked]:bg-hue-yellow-tint data-[state=checked]:border-hue-yellow-tint" />
@@ -213,7 +213,7 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl updateColumn(index, { isNullable: !!checked })} - className="border-edge data-[state=checked]:bg-blue-500 data-[state=checked]:border-blue-500" + className="border-edge data-[state=checked]:bg-hue-blue-tint data-[state=checked]:border-hue-blue-tint" />
@@ -221,7 +221,7 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl updateColumn(index, { isUnique: !!checked })} - className="border-edge data-[state=checked]:bg-purple-500 data-[state=checked]:border-purple-500" + className="border-edge data-[state=checked]:bg-hue-purple-tint data-[state=checked]:border-hue-purple-tint" />
@@ -229,7 +229,7 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl
- {/* Preview Section */} -
+ {/* + A terminal, not a panel: the ground is a hard `bg-black` in both themes, + so the tokens inside it have to resolve their DARK values or the light + palette paints dark-on-black. Scoped the way the login hero scopes its + own pinned-dark column (#402). + */} +
-
+
SQL Preview
Auto-generated
-
+            
               {generateSQL() || "-- Name your table to see SQL"}
             
@@ -261,7 +266,7 @@ export function CreateTableModal({ isOpen, onClose, onTableCreated }: CreateTabl @@ -1127,10 +1129,10 @@ export function DataCharts({ result, spec = null }: DataChartsProps) { Numeric: {analysis.numericFields.length} {chartType === "pie" && chartData.length > MAX_SERIES && ( - Showing top {MAX_SERIES} values + Showing top {MAX_SERIES} values )} {MULTI_SERIES_CHART_TYPES.has(chartType) && droppedYAxisCount > 0 && ( - + Showing first {MAX_SERIES} of {yAxis.length} series — see the Results grid for the rest )} diff --git a/src/components/DataImportModal.tsx b/src/components/DataImportModal.tsx index 7a83e308..89f032b9 100644 --- a/src/components/DataImportModal.tsx +++ b/src/components/DataImportModal.tsx @@ -300,7 +300,7 @@ export function DataImportModal({ isOpen, onClose, onImport, tables, databaseTyp - + {"Import Data"} {fileName && {fileName}} @@ -314,9 +314,9 @@ export function DataImportModal({ isOpen, onClose, onImport, tables, databaseTyp className={cn( "flex items-center gap-1.5 text-xs font-mediumr", step === s - ? "text-blue-400" + ? "text-brand" : idx < ["upload", "preview", "configure", "ready"].indexOf(step) - ? "text-emerald-400" + ? "text-success" : "text-fg-subtle", )} > @@ -324,9 +324,9 @@ export function DataImportModal({ isOpen, onClose, onImport, tables, databaseTyp className={cn( "w-5 h-5 rounded-full flex items-center justify-center text-[0.625rem]", step === s - ? "bg-blue-500/20 border border-blue-500/40" + ? "bg-brand-tint/20 border border-brand-tint/40" : idx < ["upload", "preview", "configure", "ready"].indexOf(step) - ? "bg-emerald-500/20 border border-emerald-500/40" + ? "bg-success-tint/20 border border-success-tint/40" : "bg-fill border border-hairline-strong", )} > @@ -354,7 +354,7 @@ export function DataImportModal({ isOpen, onClose, onImport, tables, databaseTyp onDrop={handleDrop} onDragOver={handleDragOver} onClick={() => fileInputRef.current?.click()} - className="w-full border-2 border-dashed border-hairline-strong rounded-xl p-12 text-center cursor-pointer hover:border-blue-500/30 hover:bg-blue-500/5 transition-all" + className="w-full border-2 border-dashed border-hairline-strong rounded-xl p-12 text-center cursor-pointer hover:border-brand-tint/30 hover:bg-brand-tint/5 transition-all" >

Drop a file here or click to browse

@@ -381,9 +381,9 @@ export function DataImportModal({ isOpen, onClose, onImport, tables, databaseTyp }} /> {error && ( -
- - {error} +
+ + {error}
)}
@@ -395,9 +395,9 @@ export function DataImportModal({ isOpen, onClose, onImport, tables, databaseTyp
{fileType === "json" ? ( - + ) : ( - + )}

{fileName}

@@ -458,7 +458,7 @@ export function DataImportModal({ isOpen, onClose, onImport, tables, databaseTyp