super-productivity/docs/styling-guide.md
Johannes Millan 524e505353 fix(onboarding): harden sync setup dismissal
Mark sync-based onboarding dismissal as fully complete so the hint tour does not restart on the next boot. Guard the sync setup CTA while the dialog is loading or open, cover the async races in tests, and move the onboarding z-index to a design token.
2026-06-03 19:54:14 +02:00

10 KiB
Raw Permalink Blame History

Styling Guide

Rules

  • All visual styling must use CSS variables from src/styles/_css-variables.scss — never hardcode colors, spacing, shadows, transitions, or z-index.
  • Positioning/layout is fine as plain CSS — flexbox, grid, display, position, dimensions.
  • Check src/app/ui/ first before creating new styled elements — 40+ reusable components exist.
  • Component SCSS should be minimal — shared styles belong in src/styles/components/ or as a mixin.
  • Material overlay components (menus, dialogs, tooltips) render outside component scope — style them in src/styles/components/ and add a comment in the component pointing there.

Anti-Patterns

Avoid Do Instead
Hardcoded colors (#fff, red) CSS variables (--text-color, --card-bg, --color-danger)
Hardcoded spacing (16px, 1rem) Spacing variables (--s2, --s, --s-half)
Hardcoded shadows Elevation variables (--whiteframe-shadow-*dp, --md-sys-level*)
Hardcoded transitions/durations Transition variables (--transition-standard, --transition-duration-*)
Custom z-index values Z-index variables (--z-main-header, --z-backdrop, etc.)
New styled elements without checking Check src/app/ui/ for existing reusable components first

Key Files

File Purpose
src/styles/_css-variables.scss All CSS custom properties (design tokens)
src/styles/themes.scss Material theme setup + utility classes
src/styles/page.scss Global page/body styles
src/styles/util.scss Utility classes
src/styles/components/ Global component styles (Material overrides, shared patterns)
src/styles/mixins/ Reusable SCSS mixins
src/app/ui/ 40+ reusable Angular UI components

Spacing Variables (8px Grid)

Variable Value Variable Value
--s-quarter 2px --s4 32px
--s-half 4px --s5 40px
--s 8px --s6 48px
--s2 16px --s7 56px
--s3 24px --s8 64px
// ✅ Good
padding: var(--s2) var(--s3);
gap: var(--s-half);

// ❌ Bad
padding: 16px 24px;
gap: 4px;

Color Variables

Use case Variables
Text --text-color, --text-color-muted, --text-color-most-intense
Backgrounds --bg, --card-bg, --task-c-bg, --sub-task-c-bg
Semantic --color-success (#4caf50), --color-warning (#ff9800), --color-danger (#f44336)
Material palette --palette-primary-500, --palette-accent-500, --palette-warn-500 (100900)
Overlays --c-dark-10 through --c-dark-90, --c-light-05 through --c-light-90
Alpha coefficients --border-alpha (0.12), --overlay-alpha (0.1), --muted-alpha (0.6), --separator-alpha (0.3)

Theme-Specific Values

Light theme sets: --bg: #f8f8f7, --card-bg: #ffffff, --text-color: rgb(44, 44, 44) Dark theme sets: --bg: #131314, --card-bg: var(--dark3), --text-color: rgb(230, 230, 230)

Dark elevation colors: --dark0 (rgb(0,0,0)) through --dark24 (rgb(56,56,56))

Theme-Specific Overrides in Components

@include darkTheme() {
  /* dark-only styles */
}
@include lightTheme() {
  /* light-only styles */
}

Mixins are in src/styles/mixins/_theming.scss.

Shadows & Elevation

  • --whiteframe-shadow-1dp through --whiteframe-shadow-24dp — classic Material shadows
  • --md-sys-level1 through --md-sys-level5 — Material Design 3 style

Transitions & Animations

Type Variables
Shorthands --transition-fast, --transition-standard, --transition-enter, --transition-leave
Durations --transition-duration-xs (90ms), -s (150ms), -m (225ms), -l (375ms)
Additional --transition-duration-enter (225ms), --transition-duration-leave (195ms), --page-transition-duration (225ms)
Timing --ani-standard-timing, --ani-enter-timing, --ani-leave-timing, --ani-sharp-timing

When migrating hardcoded durations, pick the nearest bucket — up to ~15% drift is acceptable for UI transitions.

Focus Ring

Keyboard-accessibility tokens for custom interactive elements. Material components keep their own focus treatment; use these for non-Material buttons, cards, and custom controls.

Variable Value
--focus-ring-width 2px
--focus-ring-offset 2px
--focus-ring-color var(--palette-primary-500)

Quickest adoption — add the .focus-ring utility class from util.scss, which applies an outline on :focus-visible only (so it doesn't fire on mouse clicks).

// opt-in per-element
.my-button:focus-visible {
  outline: var(--focus-ring-width) solid var(--focus-ring-color);
  outline-offset: var(--focus-ring-offset);
}

Z-Index Layers

Variable Value Purpose
--z-check-done 11 Task done checkbox
--z-main-header 12 Main header
--z-task-title-focus 32 Focused task title
--z-mobile-bottom-nav 50 Mobile bottom navigation
--z-side-nav 60 Side navigation
--z-backdrop 222 Backdrop overlay
--z-add-task-bar 999 Add task bar
--z-search-bar 999 Search bar
--z-onboarding-presets 999 Onboarding preset screen
--z-tour 1001 Tour overlay

Layout Variables

Variable Value Notes
--component-max-width 800px 9001000px on iPad
--side-nav-width 200px
--side-nav-width-l 400px
--bar-height-large 56px
--bar-height 48px
--bar-height-small 40px

Responsive Breakpoints

Available as CSS vars (--layout-xxxs through --layout-xl) and as SCSS mixins in src/styles/mixins/_media-queries.scss:

Breakpoint Value
xxxs 398px
xxs 450px
xs 600px
s 800px
m 1000px
l 1200px
xl 2000px

Utility Classes

Defined in src/styles/util.scss and src/styles/themes.scss:

  • Layout: .center-wrapper, .mw (max-width container)
  • Responsive: .hide-xs, .hide-xxs, .hide-gt-sm
  • Input: .show-only-on-touch-primary, .show-only-on-mouse-primary
  • Theme: .show-dark-only, .show-light-only
  • Color: .bg-primary, .bgc-accent, .color-primary, .bg-success, .bg-warning, .bg-danger
  • Effects: .milk-glass (backdrop blur)

Authoring Themes

Theme files live in src/assets/themes/*.css and are loaded at runtime.

Side nav: never apply CB-creating properties to the magic-side-nav host

The mobile drawer (.nav-sidenav) and its overlay (.nav-backdrop-mobile) are position: fixed children of the magic-side-nav host. On mobile the host shrinks to width: 0 (hostWidthSignal in magic-side-nav.component.ts).

Any property that establishes a new containing block for fixed-position descendantsbackdrop-filter, filter, transform, perspective, contain: paint|layout|strict, or a matching will-change — re-anchors the drawer and backdrop to the 0-wide host. The drawer never appears when the user taps the menu.

Apply glass/blur/tint to the inner .nav-sidenav instead:

/* ❌ Bad — collapses the mobile drawer */
body.isDarkTheme magic-side-nav {
  background: var(--my-pane);
  backdrop-filter: blur(32px);
}

/* ✅ Good — host stays visually inert */
body.isDarkTheme magic-side-nav .nav-sidenav {
  background: var(--my-pane);
  backdrop-filter: blur(32px);
}

Properties that do NOT create a containing block — background, border, box-shadow, margin — are safe on the host. See velvet.css and liquid-glass.css for full reference patterns.

Global Component Styles

Located in src/styles/components/, these are needed for elements that render outside component scope:

  • _overwrite-material.scss — Material component customizations
  • _customizer-menu.scss, backdrop.scss, bottom-panel.scss
  • markdown.scss, mentions.scss, table.scss
  • fab-wrapper.scss, wrap-buttons.scss, multi-btn-wrapper.scss
  • planner-shared.scss, formly-rows.scss, scrollbars.scss