Skip to content

Commit 33f5a44

Browse files
jonmatumclaude
andauthored
chore(preset): enterprise-grade token refinements (#104)
* chore(preset): enterprise-grade token refinements + ConstructPro charts Token system: - Radius: 0.75 rem → 0.5 rem (8 px base — Linear/Vercel/Stripe standard) - Pure-white card/surface/popover on blue-tinted background for clear visual lift - Border/input: oklch(0.895 0.009 265) — more visible in data-dense UIs - Foreground tokens aligned to brand hue 258 across light and dark themes - Dark mode: card step up from 0.155 → 0.165 for perceptible elevation - Typography: Inter first in --font-sans, font-weight tokens, --tracking-widest - preset.css: wire font-weight + tracking-widest into @theme - tokens/index.ts: add 'widest' to trackingTokens ConstructPro dashboard: - Export ChartConfig type from @jonmatum/next-shell/primitives - Expense-by-category donut chart (5 BudgetCategory buckets, chart-1…5 palette) - Budget vs. executed horizontal bar chart (up to 6 active projects) - recharts added as explicit dep in @jonmatum/construct-pro All 712 tests pass · lint clean · typecheck clean Refs #13 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: remove construct-pro example app The construct-pro demo app was a temporary showcase built during token refinement. The existing example app and TaskFlow already cover all library features. Removing to reduce workspace noise. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent c8d7fc2 commit 33f5a44

6 files changed

Lines changed: 165 additions & 50 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"lint-staged": "^15.2.10",
4747
"prettier": "^3.4.2",
4848
"prettier-plugin-tailwindcss": "^0.6.9",
49+
"tsx": "^4.0.0",
4950
"typescript": "^5.6.3",
5051
"typescript-eslint": "^8.16.0"
5152
},

packages/next-shell/src/primitives/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export {
6262
ChartTooltip,
6363
ChartTooltipContent,
6464
} from './chart.js';
65+
export type { ChartConfig } from './chart.js';
6566
export {
6667
Card,
6768
CardAction,

packages/next-shell/src/styles/preset.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@
118118
--tracking-normal: var(--tracking-normal);
119119
--tracking-wide: var(--tracking-wide);
120120
--tracking-wider: var(--tracking-wider);
121+
--tracking-widest: var(--tracking-widest);
122+
123+
/* ─── Font weights ───────────────────────────────────────────────── */
124+
--font-weight-normal: var(--font-weight-normal);
125+
--font-weight-medium: var(--font-weight-medium);
126+
--font-weight-semibold: var(--font-weight-semibold);
127+
--font-weight-bold: var(--font-weight-bold);
121128

122129
/* ─── Motion ─────────────────────────────────────────────────────── */
123130
--animate-duration-instant: var(--duration-instant);

packages/next-shell/src/styles/tokens.css

Lines changed: 74 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -45,32 +45,37 @@
4545
* 1. Color — light theme
4646
*
4747
* Brand palette: a barely-perceptible blue tint on neutral surfaces ties
48-
* the light mode to the navy dark theme (brand hue 258 °). Primary is the
48+
* the light mode to the navy dark theme (brand hue 258°). Primary is the
4949
* same brand blue used across jonmatum.dev / jonmatum.com.
50+
*
51+
* Pure-white card/surface/popover surfaces float above the blue-tinted
52+
* background, creating the visual lift characteristic of enterprise SaaS
53+
* (Linear, Vercel, Stripe). Foreground is aligned to the brand hue for
54+
* subtle warmth without visible color cast.
5055
* ───────────────────────────────────────────────────────────────────── */
5156
--background: oklch(0.975 0.004 265);
52-
--foreground: oklch(0.205 0.015 265);
57+
--foreground: oklch(0.19 0.018 258);
5358

54-
--surface: oklch(0.99 0.002 265);
55-
--surface-foreground: oklch(0.205 0.015 265);
59+
--surface: oklch(1 0 0);
60+
--surface-foreground: oklch(0.19 0.018 258);
5661

57-
--card: oklch(0.993 0.002 265);
58-
--card-foreground: oklch(0.205 0.015 265);
62+
--card: oklch(1 0 0);
63+
--card-foreground: oklch(0.19 0.018 258);
5964

60-
--popover: oklch(0.993 0.002 265);
61-
--popover-foreground: oklch(0.205 0.015 265);
65+
--popover: oklch(1 0 0);
66+
--popover-foreground: oklch(0.19 0.018 258);
6267

63-
--muted: oklch(0.955 0.006 265);
64-
--muted-foreground: oklch(0.46 0.015 265);
68+
--muted: oklch(0.953 0.007 265);
69+
--muted-foreground: oklch(0.44 0.018 258);
6570

66-
--accent: oklch(0.94 0.012 258);
67-
--accent-foreground: oklch(0.205 0.015 265);
71+
--accent: oklch(0.943 0.016 258);
72+
--accent-foreground: oklch(0.19 0.018 258);
6873

6974
--primary: oklch(0.52 0.2 258);
7075
--primary-foreground: oklch(0.985 0 0);
7176

72-
--secondary: oklch(0.95 0.006 265);
73-
--secondary-foreground: oklch(0.24 0.015 265);
77+
--secondary: oklch(0.952 0.007 265);
78+
--secondary-foreground: oklch(0.22 0.018 258);
7479

7580
--destructive: oklch(0.577 0.245 27.325);
7681
--destructive-foreground: oklch(0.985 0 0);
@@ -79,13 +84,15 @@
7984
--success-foreground: oklch(0.985 0 0);
8085

8186
--warning: oklch(0.78 0.17 85);
82-
--warning-foreground: oklch(0.205 0.015 265);
87+
--warning-foreground: oklch(0.19 0.018 258);
8388

8489
--info: oklch(0.539 0.13 230);
8590
--info-foreground: oklch(0.985 0 0);
8691

87-
--border: oklch(0.915 0.004 265);
88-
--input: oklch(0.9 0.006 265);
92+
/* Visible borders for data-dense enterprise UIs (tables, forms, panels).
93+
Slightly more chroma than the old value ties them to the brand hue. */
94+
--border: oklch(0.895 0.009 265);
95+
--input: oklch(0.895 0.009 265);
8996
--ring: oklch(0.52 0.2 258);
9097

9198
/* Overlay — modal backdrop scrim. Kept consistently dark in both themes
@@ -96,13 +103,13 @@
96103

97104
/* Sidebar — blue-tinted surface reinforces brand hue; primary wired to the
98105
brand blue so active nav items use the same accent as dark mode. */
99-
--sidebar: oklch(0.96 0.008 265);
100-
--sidebar-foreground: oklch(0.205 0.015 265);
106+
--sidebar: oklch(0.963 0.008 258);
107+
--sidebar-foreground: oklch(0.19 0.018 258);
101108
--sidebar-primary: oklch(0.52 0.2 258);
102109
--sidebar-primary-foreground: oklch(0.985 0 0);
103110
--sidebar-accent: oklch(0.935 0.012 258);
104-
--sidebar-accent-foreground: oklch(0.205 0.015 265);
105-
--sidebar-border: oklch(0.9 0.006 265);
111+
--sidebar-accent-foreground: oklch(0.19 0.018 258);
112+
--sidebar-border: oklch(0.895 0.009 265);
106113
--sidebar-ring: oklch(0.52 0.2 258);
107114

108115
/* Chart colors — 5 perceptually distinct hues for categorical data. */
@@ -115,22 +122,29 @@
115122
/* ─────────────────────────────────────────────────────────────────────
116123
* 2. Radius — scale driven by a single `--radius` base so brand
117124
* overrides only need to change one variable.
125+
*
126+
* 8 px base (0.5 rem) matches the professional enterprise convention
127+
* used by Linear, Vercel, and Stripe — friendly but not bubbly.
118128
* ───────────────────────────────────────────────────────────────────── */
119-
--radius: 0.75rem;
120-
--radius-xs: calc(var(--radius) - 6px);
121-
--radius-sm: calc(var(--radius) - 4px);
122-
--radius-md: calc(var(--radius) - 2px);
123-
--radius-lg: var(--radius);
129+
--radius: 0.5rem;
130+
--radius-xs: calc(var(--radius) - 4px);
131+
--radius-sm: calc(var(--radius) - 2px);
132+
--radius-md: var(--radius);
133+
--radius-lg: calc(var(--radius) + 2px);
124134
--radius-xl: calc(var(--radius) + 4px);
125135
--radius-2xl: calc(var(--radius) + 8px);
126136
--radius-full: 9999px;
127137

128138
/* ─────────────────────────────────────────────────────────────────────
129139
* 3. Typography
140+
*
141+
* Inter is listed first so consumers who load it via next/font/google
142+
* get it automatically without any additional configuration.
130143
* ───────────────────────────────────────────────────────────────────── */
131144
--font-sans:
132-
ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
133-
'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
145+
'Inter', 'Inter Variable', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
146+
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji',
147+
'Segoe UI Emoji';
134148
--font-mono:
135149
ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', 'Courier New',
136150
monospace;
@@ -158,6 +172,15 @@
158172
--tracking-normal: 0;
159173
--tracking-wide: 0.02em;
160174
--tracking-wider: 0.04em;
175+
--tracking-widest: 0.08em;
176+
177+
/* Font weights — exposed as CSS variables so custom components can use
178+
them without hardcoding numeric values. Tailwind utilities (font-bold
179+
etc.) still work as normal. */
180+
--font-weight-normal: 400;
181+
--font-weight-medium: 500;
182+
--font-weight-semibold: 600;
183+
--font-weight-bold: 700;
161184

162185
/* ─────────────────────────────────────────────────────────────────────
163186
* 4. Motion — Material-inspired standard easings, Radix-inspired speeds.
@@ -177,12 +200,12 @@
177200
* 5. Elevation — shadows tuned for a white surface. Dark theme overrides
178201
* these with higher opacity so they remain visible on dark surfaces.
179202
* ───────────────────────────────────────────────────────────────────── */
180-
--shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.06);
181-
--shadow-sm: 0 1px 3px 0 oklch(0 0 0 / 0.08), 0 1px 2px -1px oklch(0 0 0 / 0.12);
182-
--shadow-md: 0 4px 8px -2px oklch(0 0 0 / 0.1), 0 2px 4px -2px oklch(0 0 0 / 0.08);
183-
--shadow-lg: 0 12px 20px -4px oklch(0 0 0 / 0.12), 0 4px 8px -4px oklch(0 0 0 / 0.08);
184-
--shadow-xl: 0 20px 28px -6px oklch(0 0 0 / 0.14), 0 10px 14px -6px oklch(0 0 0 / 0.08);
185-
--shadow-2xl: 0 25px 50px -12px oklch(0 0 0 / 0.3);
203+
--shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.05);
204+
--shadow-sm: 0 1px 3px 0 oklch(0 0 0 / 0.07), 0 1px 2px -1px oklch(0 0 0 / 0.1);
205+
--shadow-md: 0 4px 8px -2px oklch(0 0 0 / 0.09), 0 2px 4px -2px oklch(0 0 0 / 0.06);
206+
--shadow-lg: 0 12px 20px -4px oklch(0 0 0 / 0.1), 0 4px 8px -4px oklch(0 0 0 / 0.06);
207+
--shadow-xl: 0 20px 28px -6px oklch(0 0 0 / 0.12), 0 10px 14px -6px oklch(0 0 0 / 0.07);
208+
--shadow-2xl: 0 25px 50px -12px oklch(0 0 0 / 0.28);
186209

187210
/* ─────────────────────────────────────────────────────────────────────
188211
* 6. Density — used by components for padding / gap. Consumer apps can
@@ -200,30 +223,32 @@
200223
* ───────────────────────────────────────────────────────────────────── */
201224
[data-theme='dark'] {
202225
/* Background matches the #0b0f14 navy-black brand color from jonmatum.dev.
203-
All layered surfaces step up in lightness to stay visually distinct. */
226+
All layered surfaces step up in lightness to stay visually distinct.
227+
Card is explicitly lighter than surface so elevation is perceptible
228+
even at low ambient light levels. */
204229
--background: oklch(0.095 0.015 265);
205-
--foreground: oklch(0.93 0.006 265);
230+
--foreground: oklch(0.94 0.007 258);
206231

207-
--surface: oklch(0.145 0.014 265);
208-
--surface-foreground: oklch(0.93 0.006 265);
232+
--surface: oklch(0.148 0.015 258);
233+
--surface-foreground: oklch(0.94 0.007 258);
209234

210-
--card: oklch(0.155 0.016 262);
211-
--card-foreground: oklch(0.93 0.006 265);
235+
--card: oklch(0.165 0.018 258);
236+
--card-foreground: oklch(0.94 0.007 258);
212237

213-
--popover: oklch(0.155 0.016 262);
214-
--popover-foreground: oklch(0.93 0.006 265);
238+
--popover: oklch(0.165 0.018 258);
239+
--popover-foreground: oklch(0.94 0.007 258);
215240

216241
--muted: oklch(0.2 0.015 262);
217-
--muted-foreground: oklch(0.62 0.01 265);
242+
--muted-foreground: oklch(0.63 0.012 258);
218243

219244
--accent: oklch(0.22 0.022 258);
220-
--accent-foreground: oklch(0.93 0.006 265);
245+
--accent-foreground: oklch(0.94 0.007 258);
221246

222247
--primary: oklch(0.558 0.19 258);
223248
--primary-foreground: oklch(0.985 0 0);
224249

225250
--secondary: oklch(0.2 0.015 262);
226-
--secondary-foreground: oklch(0.93 0.006 265);
251+
--secondary-foreground: oklch(0.94 0.007 258);
227252

228253
--destructive: oklch(0.576 0.191 22.216);
229254
--destructive-foreground: oklch(0.985 0 0);
@@ -237,19 +262,19 @@
237262
--info: oklch(0.542 0.12 230);
238263
--info-foreground: oklch(0.985 0 0);
239264

240-
--border: oklch(0.22 0.016 262);
241-
--input: oklch(0.26 0.018 262);
265+
--border: oklch(0.225 0.018 258);
266+
--input: oklch(0.265 0.02 258);
242267
--ring: oklch(0.558 0.19 258);
243268

244269
/* Scrim stays dark in both themes. */
245270
--overlay: oklch(0 0 0 / 0.6);
246271

247272
--sidebar: oklch(0.075 0.012 265);
248-
--sidebar-foreground: oklch(0.93 0.006 265);
273+
--sidebar-foreground: oklch(0.94 0.007 258);
249274
--sidebar-primary: oklch(0.558 0.19 258);
250275
--sidebar-primary-foreground: oklch(0.985 0 0);
251276
--sidebar-accent: oklch(0.18 0.02 258);
252-
--sidebar-accent-foreground: oklch(0.93 0.006 265);
277+
--sidebar-accent-foreground: oklch(0.94 0.007 258);
253278
--sidebar-border: oklch(0.2 0.014 262);
254279
--sidebar-ring: oklch(0.558 0.19 258);
255280

packages/next-shell/src/tokens/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export type TextSizeToken = (typeof textSizeTokens)[number];
110110
export const leadingTokens = ['tight', 'snug', 'normal', 'relaxed', 'loose'] as const;
111111
export type LeadingToken = (typeof leadingTokens)[number];
112112

113-
export const trackingTokens = ['tight', 'normal', 'wide', 'wider'] as const;
113+
export const trackingTokens = ['tight', 'normal', 'wide', 'wider', 'widest'] as const;
114114
export type TrackingToken = (typeof trackingTokens)[number];
115115

116116
/* ────────────────────────────────────────────────────────────────────────

pnpm-lock.yaml

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)