Next.js — design system
The DropAFinder dashboard uses a custom SCSS design system — no Tailwind, no component library. Styles live in src/sass/ (globals) and in .module.scss files co-located with components. The visitor-facing finder widget has a separate token system defined per-finder via appearanceThemes.ts in the V2 builder config.
Token system
Tokens are CSS custom properties defined in src/sass/globals.scss. They are surfaced to SCSS as $v2-* variables via the bridge file src/sass/_v2-tokens.scss. The bridge pattern means tokens are defined once in :root/[data-theme] but can be used in CSS Modules (which can’t emit :root rules themselves).
Color tokens
All color tokens have a --v2- prefix. There are 34 tokens covering:
| Group | Tokens |
|---|---|
| Text | --v2-text-1 (primary), --v2-text-2 (secondary), --v2-text-3 (tertiary), --v2-muted |
| Surfaces | --v2-bg-page, --v2-surface, --v2-bg-raised, --v2-bg-subtle |
| Brand | --v2-navy, --v2-navy-hover, --v2-blue, --v2-cyan, --v2-ink |
| Borders | --v2-border, --v2-line |
| Tints | --v2-navy-tint, --v2-navy-tint-hover, --v2-navy-light, --v2-blue-tint, --v2-cyan-dim |
| Status | --v2-ok, --v2-warn, --v2-danger |
| Feedback | --v2-success, --v2-success-bg, --v2-warning, --v2-warning-bg, --v2-error, --v2-error-bg |
Use $v2-* SCSS variables in component files. Never hardcode hex values.
Spacing and layout tokens
Two layout utility properties on :root:
--app-pad— responsive outer padding (clamp(1.6rem, 1.2rem + 1.6vw, 3.2rem))--app-gap— responsive gap between sections (clamp(1.2rem, 0.8rem + 1.2vw, 2.4rem))--surface-radius— card/panel border-radius (1.4rem)--surface-shadow— standard elevation shadow
Light / dark theming
The theme toggle is implemented with next-themes. It sets a data-theme attribute on <html>. The [data-theme='dark'] selector in globals.scss overrides all --v2-* custom properties with dark equivalents.
Light mode dark values map to #1A1D38 primary text and #F7F8FC page background. Dark mode inverts to #E8EAFF primary text and #0F1117 page background with rgba-based borders.
No JavaScript is needed to switch tokens — CSS handles the swap via the attribute selector. Components that need theme awareness can use useTheme() from next-themes for programmatic access.
SCSS file structure
| File | Contents |
|---|---|
globals.scss | :root + [data-theme='dark'] token definitions; imports all partials |
_v2-tokens.scss | Bridge: SCSS $v2-* variables pointing to CSS custom properties |
_colors.scss | Utility color classes |
_typography.scss | Body, heading, label styles |
_buttons.scss | .btn, .btn-primary, .btn-secondary, etc. |
_forms.scss | Input, select, textarea, form group styles |
_reset.scss | CSS reset |
_mixins.scss | Shared SCSS mixins (e.g., responsive breakpoints) |
_settings.scss | SCSS variables for breakpoints, spacing scale |
Component styling pattern
- CSS Modules (
.module.scss) for component-scoped styles. - BEM class names inside modules.
- Import the module and reference with
styles['block__element']orstyles['block__element--modifier']. - Use
clsxfor conditional class combinations. - Access design tokens via
$v2-*variables imported from_v2-tokens.scss.
@use '@/sass/v2-tokens' as *;
.card {
background: $v2-bg-raised;
border: 1px solid $v2-border;
border-radius: var(--surface-radius);
}
.card__title {
color: $v2-text-1;
}Semi-transparent colors
When a dedicated token doesn’t exist for a transparency variant, use color-mix():
color: color-mix(in srgb, #{$v2-navy} 15%, transparent);Related pages
- Next.js: Conventions — BEM enforcement, SCSS rules from
AGENTS.md - Next.js: Directory structure — where
sass/sits in the tree