Skip to content
This repository was archived by the owner on Apr 24, 2026. It is now read-only.

Commit 505a50e

Browse files
authored
Merge pull request #41 from renderghost/feature/issue-40-theme-tokens
Feature #40: Simplify and organize theme color tokens
2 parents 0d50592 + a53ba72 commit 505a50e

10 files changed

Lines changed: 209 additions & 152 deletions

File tree

src/app/[handle]/page.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ export default async function ProfilePage({ params }: PageProps) {
2626
return (
2727
<main className="flex min-h-screen flex-col items-center justify-center p-6">
2828
<div className="text-center">
29-
<h1 className="text-2xl font-bold mb-4">Invalid Handle</h1>
30-
<p className="text-gray-600 mb-6">
29+
<h1 className="text-2xl font-bold mb-4 leading-tight">Invalid Handle</h1>
30+
<p className="text-muted-foreground mb-6 leading-relaxed">
3131
The handle provided is not valid.
3232
</p>
33-
<a href="/" className="text-blue-600 hover:underline">
33+
<a href="/" className="text-primary hover:underline">
3434
Go to homepage
3535
</a>
3636
</div>
@@ -133,11 +133,11 @@ export default async function ProfilePage({ params }: PageProps) {
133133
return (
134134
<main className="flex min-h-screen flex-col items-center justify-center p-6">
135135
<div className="text-center">
136-
<h1 className="text-2xl font-bold mb-4">Error Loading Profile</h1>
137-
<p className="text-gray-600 mb-6">
136+
<h1 className="text-2xl font-bold mb-4 leading-tight">Error Loading Profile</h1>
137+
<p className="text-muted-foreground mb-6 leading-relaxed">
138138
Unable to load this profile. Please try again later.
139139
</p>
140-
<a href="/" className="text-blue-600 hover:underline">
140+
<a href="/" className="text-primary hover:underline">
141141
Go to homepage
142142
</a>
143143
</div>

src/app/dashboard/profile/edit/page.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ export default async function EditProfilePage() {
3636
};
3737

3838
return (
39-
<main className="min-h-screen bg-gray-50">
39+
<main className="min-h-screen bg-background">
4040
{/* Header */}
41-
<div className="bg-white border-b border-gray-200">
41+
<div className="bg-card border-b border-border">
4242
<div className="max-w-2xl mx-auto px-4 py-4">
4343
<Link
4444
href="/dashboard"
45-
className="inline-flex items-center text-sm text-gray-600 hover:text-gray-900 mb-2"
45+
className="inline-flex items-center text-sm text-muted-foreground hover:text-foreground mb-2 leading-tight"
4646
>
4747
<svg
4848
className="w-4 h-4 mr-1"
@@ -61,14 +61,14 @@ export default async function EditProfilePage() {
6161
</Link>
6262
<div className="flex items-center justify-between">
6363
<div>
64-
<h1 className="text-xl font-bold">Edit Profile</h1>
65-
<p className="text-sm text-gray-600 mt-1">
64+
<h1 className="text-xl font-bold leading-tight">Edit Profile</h1>
65+
<p className="text-sm text-muted-foreground mt-1 leading-relaxed">
6666
Update your researcher profile information
6767
</p>
6868
</div>
6969
<Link
7070
href={`/${profile.handle}`}
71-
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
71+
className="px-4 py-2 text-sm font-medium text-foreground bg-card border border-border rounded-lg hover:bg-muted transition-colors"
7272
>
7373
View Profile
7474
</Link>

src/app/globals.css

Lines changed: 140 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,91 @@
33

44
@custom-variant dark (@media (prefers-color-scheme: dark));
55

6+
/* =============================================================================
7+
CORE COLOR TOKENS
8+
Foundational color palette using Tailwind naming convention.
9+
These are the source of truth - semantic tokens reference these.
10+
============================================================================= */
11+
12+
:root {
13+
/* Mono - Neutrals */
14+
--color-mono-white: hsl(0 0% 100%);
15+
--color-mono-black: hsl(0 0% 0%);
16+
17+
/* Purple Muted - Low saturation (10-40%), sorted by luminance light→dark
18+
Use for: backgrounds, text, borders, neutral elements */
19+
--color-purple-muted-50: oklch(0.958 0.009 292.95);
20+
--color-purple-muted-100: oklch(0.924 0.016 293.82);
21+
--color-purple-muted-200: oklch(0.846 0.032 293.73);
22+
--color-purple-muted-300: oklch(0.772 0.045 292.49);
23+
--color-purple-muted-400: oklch(0.694 0.061 292.38);
24+
--color-purple-muted-500: oklch(0.618 0.071 292.02);
25+
--color-purple-muted-600: oklch(0.539 0.079 291.62);
26+
--color-purple-muted-700: oklch(0.438 0.083 290.67);
27+
--color-purple-muted-800: oklch(0.341 0.069 291.51);
28+
--color-purple-muted-900: oklch(0.238 0.049 291.04);
29+
--color-purple-muted-950: oklch(0.19 0.042 290.64);
30+
31+
/* Purple Vivid - High saturation (75-100%), sorted by luminance light→dark
32+
Use for: links, buttons, accents, interactive elements */
33+
--color-purple-vivid-50: oklch(0.96 0.019 289.4);
34+
--color-purple-vivid-100: oklch(0.897 0.05 290.33);
35+
--color-purple-vivid-200: oklch(0.762 0.118 288.81);
36+
--color-purple-vivid-300: oklch(0.646 0.176 286.77);
37+
--color-purple-vivid-400: oklch(0.532 0.228 282.17);
38+
--color-purple-vivid-500: oklch(0.451 0.258 275.99);
39+
--color-purple-vivid-600: oklch(0.382 0.224 274.42);
40+
--color-purple-vivid-700: oklch(0.312 0.184 273.59);
41+
--color-purple-vivid-800: oklch(0.238 0.138 273.57);
42+
--color-purple-vivid-900: oklch(0.157 0.086 275.83);
43+
--color-purple-vivid-950: oklch(0.11 0.059 278.79);
44+
45+
/* Red - Destructive actions */
46+
--color-red-muted-400: hsl(350 88% 75%);
47+
48+
/* Yellow - Focus ring in dark mode */
49+
--color-yellow-vivid-500: hsl(65 100% 50%);
50+
--yell: hsl(65 100% 50%);
51+
}
52+
53+
/* =============================================================================
54+
LIGHT MODE SEMANTIC TOKENS
55+
Map semantic names to core tokens for light theme.
56+
============================================================================= */
57+
658
:root {
7-
--background: hsl(250 40.0000% 98.0392%);
8-
--foreground: hsl(250 32.5843% 17.4510%);
9-
--card: hsl(0 0% 100%);
10-
--card-foreground: hsl(250 38.7755% 9.6078%);
11-
--popover: hsl(0 0% 100%);
12-
--popover-foreground: hsl(250 38.7755% 9.6078%);
13-
--primary: hsl(250 74.9020% 50%);
14-
--primary-foreground: hsl(0 0% 100%);
15-
--secondary: hsl(250 13.0435% 90.9804%);
16-
--secondary-foreground: hsl(250 19.1176% 26.6667%);
17-
--muted: hsl(250 9.8592% 86.0784%);
18-
--muted-foreground: hsl(250 16.3934% 23.9216%);
19-
--accent: hsl(250 100% 93.9216%);
20-
--accent-foreground: hsl(250 18.8406% 27.0588%);
21-
--destructive: hsl(350 87.5000% 74.9020%);
22-
--destructive-foreground: hsl(250 38.7755% 9.6078%);
23-
--border: hsl(250 16.0622% 62.1569%);
24-
--input: hsl(250 9.8592% 86.0784%);
25-
--ring: hsl(250.0524 74.9020% 50%);
26-
--chart-1: hsl(250 83.5294% 66.6667%);
27-
--chart-2: hsl(250 75.3555% 58.6275%);
28-
--chart-3: hsl(250 57.9365% 50.5882%);
29-
--chart-4: hsl(250 54.5024% 41.3725%);
30-
--chart-5: hsl(250 47.4286% 34.3137%);
31-
--sidebar: hsl(250 22.5806% 93.9216%);
32-
--sidebar-foreground: hsl(250 38.7755% 9.6078%);
33-
--sidebar-primary: hsl(250 74.9020% 50%);
34-
--sidebar-primary-foreground: hsl(0 0% 100%);
35-
--sidebar-accent: hsl(250 20.0000% 97.0588%);
36-
--sidebar-accent-foreground: hsl(250 38.7755% 9.6078%);
37-
--sidebar-border: hsl(250 16.0622% 62.1569%);
38-
--sidebar-ring: hsl(250 74.9020% 50%);
59+
--background: var(--color-purple-muted-50);
60+
--foreground: var(--color-purple-muted-950);
61+
--card: var(--color-mono-white);
62+
--card-foreground: var(--color-purple-muted-1000);
63+
--popover: var(--color-mono-white);
64+
--popover-foreground: var(--color-purple-muted-1000);
65+
--primary: var(--color-purple-vivid-800);
66+
--primary-foreground: var(--color-mono-white);
67+
--secondary: var(--color-purple-muted-300);
68+
--secondary-foreground: var(--color-purple-muted-900);
69+
--muted: var(--color-purple-muted-400);
70+
--muted-foreground: var(--color-purple-muted-950);
71+
--accent: var(--color-purple-vivid-50);
72+
--accent-foreground: var(--color-purple-muted-900);
73+
--destructive: var(--color-red-400);
74+
--destructive-foreground: var(--color-purple-muted-1000);
75+
--border: var(--color-purple-muted-700);
76+
--input: var(--color-purple-muted-400);
77+
--ring: var(--color-purple-vivid-800);
78+
--chart-1: var(--color-purple-vivid-500);
79+
--chart-2: var(--color-purple-vivid-600);
80+
--chart-3: var(--color-purple-vivid-700);
81+
--chart-4: var(--color-purple-vivid-900);
82+
--chart-5: var(--color-purple-vivid-950);
83+
--sidebar: var(--color-purple-muted-200);
84+
--sidebar-foreground: var(--color-purple-muted-1000);
85+
--sidebar-primary: var(--color-purple-vivid-800);
86+
--sidebar-primary-foreground: var(--color-mono-white);
87+
--sidebar-accent: var(--color-purple-muted-100);
88+
--sidebar-accent-foreground: var(--color-purple-muted-1000);
89+
--sidebar-border: var(--color-purple-muted-700);
90+
--sidebar-ring: var(--color-purple-vivid-800);
3991
--font-sans: 'DM Sans', ui-sans-serif, sans-serif, system-ui;
4092
--font-serif: 'DM Serif Text', ui-serif, serif;
4193
--font-mono: 'DM Mono', ui-monospace, monospace;
@@ -45,7 +97,7 @@
4597
--shadow-blur: 0px;
4698
--shadow-spread: 0px;
4799
--shadow-opacity: 0;
48-
--shadow-color: hsl(0 0% 0%);
100+
--shadow-color: var(--color-mono-black);
49101
--shadow-2xs: 0px 0px 0px 0px hsl(0 0% 0% / 0);
50102
--shadow-xs: 0px 0px 0px 0px hsl(0 0% 0% / 0);
51103
--shadow-sm: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
@@ -58,58 +110,63 @@
58110
--spacing: 0.25rem;
59111
}
60112

113+
/* =============================================================================
114+
DARK MODE SEMANTIC TOKENS
115+
Map semantic names to core tokens for dark theme.
116+
============================================================================= */
117+
61118
@media (prefers-color-scheme: dark) {
62119
:root {
63-
--background: hsl(250 46% 10%);
64-
--foreground: hsl(250 61% 90%);
65-
--card: hsl(250 33.3333% 16%);
66-
--card-foreground: hsl(250 61% 90%);
67-
--popover: hsl(250 33.3333% 16%);
68-
--popover-foreground: hsl(250 30% 90.9804%);
69-
--primary: hsl(250 88.8889% 76%);
70-
--primary-foreground: hsl(250 46% 10%);
71-
--secondary: hsl(250 24% 24%);
72-
--secondary-foreground: hsl(250 80% 90%);
73-
--muted: hsl(250 40% 13.9216%);
74-
--muted-foreground: hsl(250 12% 65.0980%);
75-
--accent: hsl(250 18.8406% 27.0588%);
76-
--accent-foreground: hsl(250 12.1951% 83.9216%);
77-
--destructive: hsl(350 86.7769% 76.2745%);
78-
--destructive-foreground: hsl(249.4737 38.7755% 9.6078%);
79-
--border: hsl(250.0000 13.7931% 34.1176%);
80-
--input: hsl(250.0000 13.7931% 34.1176%);
81-
--ring: hsl(64.9412 100% 50%);
82-
--chart-1: hsl(250 89.4737% 73.9216%);
83-
--chart-2: hsl(250 83.5294% 66.6667%);
84-
--chart-3: hsl(250 75.3555% 58.6275%);
85-
--chart-4: hsl(250 57.9365% 50.5882%);
86-
--chart-5: hsl(250 54.5024% 41.3725%);
87-
--sidebar: hsl(250 33.3333% 16%);
88-
--sidebar-foreground: hsl(250 30% 90.9804%);
89-
--sidebar-primary: hsl(250 88.8889% 76%);
90-
--sidebar-primary-foreground: hsl(250 46% 10%);
91-
--sidebar-accent: hsl(250 18.8406% 27.0588%);
92-
--sidebar-accent-foreground: hsl(250 12.1951% 83.9216%);
93-
--sidebar-border: hsl(250 13.7931% 34.1176%);
94-
--sidebar-ring: hsl(64.9412 100% 50%);
95-
--font-sans: 'DM Sans', ui-sans-serif, sans-serif, system-ui;
96-
--font-serif: 'DM Serif Text', ui-serif, serif;
97-
--font-mono: 'DM Mono', ui-monospace, monospace;
98-
--radius: 0.375rem;
99-
--shadow-x: 0px;
100-
--shadow-y: 0px;
101-
--shadow-blur: 0px;
102-
--shadow-spread: 0px;
103-
--shadow-opacity: 0;
104-
--shadow-color: hsl(0 0% 0%);
105-
--shadow-2xs: 0px 0px 0px 0px hsl(0 0% 0% / 0);
106-
--shadow-xs: 0px 0px 0px 0px hsl(0 0% 0% / 0);
107-
--shadow-sm: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
108-
--shadow: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
109-
--shadow-md: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 2px 4px -1px hsl(0 0% 0% / 0);
110-
--shadow-lg: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 4px 6px -1px hsl(0 0% 0% / 0);
111-
--shadow-xl: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 8px 10px -1px hsl(0 0% 0% / 0);
112-
--shadow-2xl: 0px 0px 0px 0px hsl(0 0% 0% / 0);
120+
--background: var(--color-purple-vivid-950);
121+
--foreground: var(--color-purple-vivid-50);
122+
--card: var(--color-purple-muted-950);
123+
--card-foreground: var(--color-purple-vivid-50);
124+
--popover: var(--color-purple-vivid-950);
125+
--popover-foreground: var(--color-purple-vivid-50);
126+
--primary: var(--color-purple-vivid-500);
127+
--primary-foreground: var(--color-purple-vivid-50);
128+
--secondary: var(--color-purple-muted-700);
129+
--secondary-foreground: var(--color-purple-vivid-50);
130+
--muted: var(--color-purple-muted-800);
131+
--muted-foreground: var(--color-purple-muted-400);
132+
--accent: var(--color-purple-muted-900);
133+
--accent-foreground: var(--color-purple-muted-500);
134+
--destructive: var(--color-red-muted-400);
135+
--destructive-foreground: var(--color-purple-muted-900);
136+
--border: var(--color-purple-muted-800);
137+
--input: var(--color-purple-muted-700);
138+
--ring: var(--color-yellow-500);
139+
--chart-1: var(--color-purple-vivid-400);
140+
--chart-2: var(--color-purple-vivid-500);
141+
--chart-3: var(--color-purple-vivid-600);
142+
--chart-4: var(--color-purple-vivid-700);
143+
--chart-5: var(--color-purple-vivid-900);
144+
--sidebar: var(--color-purple-vivid-950);
145+
--sidebar-foreground: var(--color-purple-muted-100);
146+
--sidebar-primary: var(--color-purple-vivid-500);
147+
--sidebar-primary-foreground: var(--color-purple-muted-1000);
148+
--sidebar-accent: var(--color-purple-muted-900);
149+
--sidebar-accent-foreground: var(--color-purple-muted-500);
150+
--sidebar-border: var(--color-purple-muted-800);
151+
--sidebar-ring: var(--color-yellow-500);
152+
--font-sans: 'DM Sans', ui-sans-serif, sans-serif, system-ui;
153+
--font-serif: 'DM Serif Text', ui-serif, serif;
154+
--font-mono: 'DM Mono', ui-monospace, monospace;
155+
--radius: 0.375rem;
156+
--shadow-x: 0px;
157+
--shadow-y: 0px;
158+
--shadow-blur: 0px;
159+
--shadow-spread: 0px;
160+
--shadow-opacity: 0;
161+
--shadow-color: var(--color-mono-black);
162+
--shadow-2xs: 0px 0px 0px 0px hsl(0 0% 0% / 0);
163+
--shadow-xs: 0px 0px 0px 0px hsl(0 0% 0% / 0);
164+
--shadow-sm: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
165+
--shadow: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 1px 2px -1px hsl(0 0% 0% / 0);
166+
--shadow-md: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 2px 4px -1px hsl(0 0% 0% / 0);
167+
--shadow-lg: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 4px 6px -1px hsl(0 0% 0% / 0);
168+
--shadow-xl: 0px 0px 0px 0px hsl(0 0% 0% / 0), 0px 8px 10px -1px hsl(0 0% 0% / 0);
169+
--shadow-2xl: 0px 0px 0px 0px hsl(0 0% 0% / 0);
113170
}
114171
}
115172

src/app/page.tsx

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,60 +2,60 @@ import Link from 'next/link';
22

33
export default function Home() {
44
return (
5-
<main className="flex min-h-screen flex-col items-center justify-center p-6 bg-linear-to-b from-white to-gray-50">
5+
<main className="flex min-h-screen flex-col items-center justify-center p-6 bg-linear-to-b from-background to-muted">
66
<div className="max-w-2xl w-full text-center">
7-
<h1 className="text-5xl font-bold mb-4 bg-linear-to-r from-blue-600 to-blue-800 bg-clip-text text-transparent">
7+
<h1 className="text-5xl font-bold mb-4 bg-linear-to-r from-primary to-primary/70 bg-clip-text text-transparent leading-tight">
88
Lanyards
99
</h1>
10-
<p className="text-xl text-gray-700 mb-2">
10+
<p className="text-xl text-foreground mb-2 leading-relaxed">
1111
Your Researcher Profile on the AT Protocol
1212
</p>
13-
<p className="text-lg text-gray-600 mb-8">
13+
<p className="text-lg text-muted-foreground mb-8 leading-relaxed">
1414
An alternative to ORCID for the decentralized web
1515
</p>
1616

1717
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-12">
1818
<Link
1919
href="/auth"
20-
className="bg-blue-600 text-white py-3 px-8 rounded-lg font-medium hover:bg-blue-700 transition-colors"
20+
className="bg-primary text-primary-foreground py-3 px-8 rounded-lg font-medium hover:bg-primary/90 transition-colors"
2121
>
2222
Get Started
2323
</Link>
2424
<Link
2525
href="/auth"
26-
className="bg-white border-2 border-gray-300 text-gray-700 py-3 px-8 rounded-lg font-medium hover:border-gray-400 transition-colors"
26+
className="bg-card border-2 border-border text-foreground py-3 px-8 rounded-lg font-medium hover:border-primary/50 transition-colors"
2727
>
2828
Sign In
2929
</Link>
3030
</div>
3131

3232
<div className="grid md:grid-cols-2 gap-6 text-left">
33-
<div className="bg-white p-6 rounded-lg shadow-sm">
34-
<h3 className="font-semibold text-lg mb-2">Mobile-First Design</h3>
35-
<p className="text-gray-600 text-sm">
33+
<div className="bg-card p-6 rounded-lg shadow-sm">
34+
<h3 className="font-semibold text-lg mb-2 leading-snug">Mobile-First Design</h3>
35+
<p className="text-muted-foreground text-sm leading-relaxed">
3636
Share your profile easily at conferences with QR code support and
3737
optimized mobile display.
3838
</p>
3939
</div>
40-
<div className="bg-white p-6 rounded-lg shadow-sm">
41-
<h3 className="font-semibold text-lg mb-2">Built on AT Protocol</h3>
42-
<p className="text-gray-600 text-sm">
40+
<div className="bg-card p-6 rounded-lg shadow-sm">
41+
<h3 className="font-semibold text-lg mb-2 leading-snug">Built on AT Protocol</h3>
42+
<p className="text-muted-foreground text-sm leading-relaxed">
4343
Your data is stored on the AT Protocol, giving you true ownership
4444
and portability.
4545
</p>
4646
</div>
47-
<div className="bg-white p-6 rounded-lg shadow-sm">
48-
<h3 className="font-semibold text-lg mb-2">
47+
<div className="bg-card p-6 rounded-lg shadow-sm">
48+
<h3 className="font-semibold text-lg mb-2 leading-snug">
4949
Comprehensive Profile
5050
</h3>
51-
<p className="text-gray-600 text-sm">
51+
<p className="text-muted-foreground text-sm leading-relaxed">
5252
Manage affiliations, publications, events, and social networks all
5353
in one place.
5454
</p>
5555
</div>
56-
<div className="bg-white p-6 rounded-lg shadow-sm">
57-
<h3 className="font-semibold text-lg mb-2">DOI Integration</h3>
58-
<p className="text-gray-600 text-sm">
56+
<div className="bg-card p-6 rounded-lg shadow-sm">
57+
<h3 className="font-semibold text-lg mb-2 leading-snug">DOI Integration</h3>
58+
<p className="text-muted-foreground text-sm leading-relaxed">
5959
Add publications by DOI and automatically fetch metadata from
6060
CrossRef.
6161
</p>

0 commit comments

Comments
 (0)