# 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 | ## Dialog action buttons One treatment per role so dialogs feel consistent. Classify by **function, not label** ("Close" that dismisses a read-only view is secondary; "Close" that commits is primary). | Role | Treatment | Notes | | -------------------------------- | --------------------------------- | ---------------------------------------------------------------- | | Primary / confirming | `mat-flat-button color="primary"` | Save, OK, Submit, Create, Schedule — the main affirmative action | | Cancel / dismiss / secondary | `mat-button` (no `color`) | De-emphasized text button | | Destructive | `color="warn"` (keep the variant) | Delete, Remove, Unschedule — never folded into primary | | Genuine alternative (not cancel) | `mat-stroked-button` | e.g. "Skip instance", "Configure" next to a primary action | Rules: - **Icons:** drop generic `check`/`close` icons on OK/Cancel/Save/Submit — they add nothing. Keep icons that carry meaning (`alarm`/`today`/`event_busy` in scheduling, `wb_sunny`, `save`, `cloud_upload`, `delete_forever`). - **No `color` on cancel/close** — leftover `color="primary"` on a Cancel just tints it; remove it. - **No dead classes** — the legacy Bootstrap `btn btn-primary` classes are gone; don't reintroduce them. `submit-button` is only styled inside `dialog-create-tag`. - **Symmetric choice dialogs** (e.g. sync "use remote" vs "use local") may use two matched `mat-stroked-button`s — there is no single primary. ## 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 | ```scss // ✅ 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` (100–900) | | 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 ```scss @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). ```scss // 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 | 900–1000px 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 variables and mixins in `src/styles/mixins/_media-queries.scss`: | Breakpoint | Value | | ---------- | ------ | | `xxxs` | 398px | | `xxs` | 440px | | `xs` | 600px | | `sm` | 960px | | `md` | 1280px | | `lg` | 1920px | | `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 descendants](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block) — `backdrop-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: ```css /* ❌ 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`