7.8 KiB
7.8 KiB
Design System
Complete global.css structure for the design system. Adapt colors, fonts, and effects to match the client's brand.
File Structure
The CSS file follows this exact order:
- Tailwind import
- Font-face declarations
- Tailwind variants and plugins
- @theme block (design tokens)
- :root variables (shadcn compatibility, optional)
- Global element styles
- Custom cursor (optional)
- Scrollbar theming
- Utility classes
- Keyframe animations
- Reduced motion resets
- Safe viewport utilities
- @theme inline block (shadcn, optional)
- Dark mode overrides (optional)
Complete Template
@import "tailwindcss";
/* ── Custom Font ── */
@font-face {
font-family: "{{DISPLAY_FONT_NAME}}";
src: url("/assets/fonts/{{FONT_FILE}}.woff2") format("woff2"),
url("/assets/fonts/{{FONT_FILE}}.ttf") format("truetype");
font-weight: normal;
font-style: normal;
font-display: swap;
}
@custom-variant dark (&:is(.dark *));
@plugin "@tailwindcss/typography";
/* ── Design Tokens ── */
@theme {
/* Brand colors — substitute from client brief */
--color-primary: {{PRIMARY_COLOR}}; /* e.g. #FF006E */
--color-primary-light: {{PRIMARY_LIGHT}}; /* lighter variant */
--color-primary-dark: {{PRIMARY_DARK}}; /* darker variant */
--color-accent: {{ACCENT_COLOR}}; /* e.g. #7B61FF */
--color-secondary: {{SECONDARY_COLOR}}; /* e.g. #00F0FF */
--color-highlight: {{HIGHLIGHT_COLOR}}; /* e.g. #FFE600 */
--color-dark: #0A0A0A;
--color-darker: #050505;
--color-gray: #1A1A1A;
/* Font stacks */
--font-display: "{{DISPLAY_FONT}}", {{DISPLAY_FALLBACK}};
--font-body: "{{BODY_FONT}}", sans-serif;
--font-mono: "{{MONO_FONT}}", monospace;
/* Pixel shadows (4px offset = retro pixel aesthetic) */
--shadow-pixel-primary: 4px 4px 0 0 {{PRIMARY_DARK}};
--shadow-pixel-primary-lg: 8px 8px 0 0 {{PRIMARY_DARK}};
--shadow-pixel-accent: 4px 4px 0 0 {{ACCENT_DARK}};
/* Glow effects */
--shadow-glow-primary: 0 0 20px {{PRIMARY_COLOR}}, 0 0 40px {{PRIMARY_COLOR}};
--shadow-glow-accent: 0 0 20px {{ACCENT_COLOR}}, 0 0 40px {{ACCENT_COLOR}};
--shadow-glow-secondary: 0 0 20px {{SECONDARY_COLOR}}, 0 0 40px {{SECONDARY_COLOR}};
}
/* ── Global Styles ── */
:root {
--cursor-size: 20px;
--radius: 0px; /* sharp pixel aesthetic — set to 0.5rem for rounded */
}
html {
cursor: none; /* hide for custom cursor — remove if not using custom cursor */
}
@media (pointer: coarse) {
html { cursor: auto; }
}
body {
font-family: var(--font-body);
background-color: var(--color-dark);
color: #ffffff;
margin: 0;
padding: 0;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-display);
text-transform: uppercase; /* remove if display font is not pixel/block style */
}
code, pre {
font-family: var(--font-mono);
}
/* ── Custom Cursor (optional — remove block if not using) ── */
#custom-cursor {
position: fixed;
top: 0;
left: 0;
width: var(--cursor-size);
height: var(--cursor-size);
border: 2px solid var(--color-primary);
pointer-events: none;
z-index: 9999;
transform: translate(-50%, -50%);
mix-blend-mode: difference;
transition: width 0.2s, height 0.2s, background-color 0.2s;
}
#custom-cursor::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 4px;
height: 4px;
background-color: var(--color-secondary);
transform: translate(-50%, -50%);
}
@media (pointer: coarse) {
#custom-cursor { display: none; }
}
/* ── Scrollbar ── */
::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track { background: var(--color-darker); }
::-webkit-scrollbar-thumb {
background: var(--color-primary-dark);
border: 2px solid var(--color-darker);
}
::-webkit-scrollbar-thumb:hover { background: var(--color-primary); }
/* ── Utility Classes ── */
.text-glow-primary { text-shadow: 0 0 10px var(--color-primary); }
.text-glow-primary-hover {
transition: text-shadow 0.3s ease, color 0.3s ease;
}
.text-glow-primary-hover:hover {
color: var(--color-primary);
text-shadow: 0 0 10px var(--color-primary);
}
.text-glow-accent { text-shadow: 0 0 10px var(--color-accent); }
.text-glow-secondary { text-shadow: 0 0 10px var(--color-secondary); }
.box-pixel { box-shadow: 4px 4px 0 0 var(--color-primary-dark); }
.box-pixel-accent { box-shadow: 4px 4px 0 0 {{ACCENT_DARK}}; }
.box-pixel-secondary { box-shadow: 4px 4px 0 0 {{SECONDARY_DARK}}; }
.pixel-art {
image-rendering: pixelated;
image-rendering: crisp-edges;
}
.scrollbar-none {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-none::-webkit-scrollbar { display: none; }
/* ── Atmospheric Overlays ── */
.scanlines { position: relative; }
.scanlines::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0, 0, 0, 0.15) 2px,
rgba(0, 0, 0, 0.15) 4px
);
pointer-events: none;
z-index: 10;
}
.crt-screen { position: relative; overflow: hidden; }
.crt-screen::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%);
background-size: 100% 4px;
pointer-events: none;
z-index: 10;
}
.crt-screen::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse at center, transparent 0%, rgba(0, 0, 0, 0.4) 100%);
pointer-events: none;
z-index: 11;
}
/* ── Keyframe Animations ── */
@keyframes vhs-flicker {
0%, 100% { opacity: 1; }
92% { opacity: 1; }
93% { opacity: 0.8; }
94% { opacity: 1; }
96% { opacity: 0.9; }
97% { opacity: 1; }
}
.vhs-flicker { animation: vhs-flicker 4s infinite; }
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
.animate-blink { animation: blink 1s infinite; }
@keyframes glitch {
0% { transform: translate(0); }
20% { transform: translate(-2px, 2px); }
40% { transform: translate(-2px, -2px); }
60% { transform: translate(2px, 2px); }
80% { transform: translate(2px, -2px); }
100% { transform: translate(0); }
}
.animate-glitch { animation: glitch 0.3s cubic-bezier(.25,.46,.45,.94) both infinite; }
@keyframes marquee-scroll {
0% { transform: translateX(0); }
100% { transform: translateX(-50%); }
}
.marquee-track { animation: marquee-scroll 20s linear infinite; }
/* ── Reduced Motion ── */
@media (prefers-reduced-motion: reduce) {
.marquee-track { animation: none; }
.vhs-flicker { animation: none; }
.animate-blink { animation: none; }
.animate-glitch { animation: none; }
}
/* ── Safe Viewport Height ── */
.h-screen-safe { height: 100vh; height: 100dvh; }
.min-h-screen-safe { min-height: 100vh; min-height: 100dvh; }
Color Token Mapping Guide
Map any brand palette to the token system:
| Token | Purpose | Example |
|---|---|---|
--color-primary |
Main brand color, CTAs, links, highlights | #FF006E |
--color-primary-light |
Hover states, lighter accents | Lighten primary 20% |
--color-primary-dark |
Shadows, pressed states, dark accents | Darken primary 20% |
--color-accent |
Secondary brand color, headings, tags | #7B61FF |
--color-secondary |
Tertiary color, links, code text | #00F0FF |
--color-highlight |
Warnings, special callouts | #FFE600 |
--color-dark |
Page backgrounds | #0A0A0A |
--color-darker |
Footer, deeper sections | #050505 |
--color-gray |
Card backgrounds, borders | #1A1A1A |
Font Strategy
- Self-host pixel/display fonts as woff2 + ttf — preload the woff2 in BaseHead
- Google Fonts for body + mono fonts — preconnect in BaseHead
- Always use
font-display: swapto prevent FOUT blocking - Preload pattern in BaseHead:
<link rel="preload" href="/assets/fonts/Font.woff2" as="font" type="font/woff2" crossorigin />