0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-07 13:19:19 +08:00
discourse/.skills/discourse-writing-html-css/references/layout-and-responsive.md
Kris 7f1cff822f
FEATURE: LLM skills for writing HTML and CSS (#40410)
This attempts to define some useful rules for LLMs to write better HTML
and CSS for Discourse... the basics covered include:

**Naming (BEM)**
- `block__element` + standalone `.--modifier` (two-dash); `is-`/`has-`
state classes
  - Name by meaning, not appearance (no position/color/size in names)
  - Don't interpolate user input into class names — use data attributes
  - Prefer a state class over `:has()`

  **Color & theming**
- Never hardcode color; use the custom-property palette; no separate
dark-mode block
- `--token-color-*` for standard UI, palette for bespoke; `--d-*` design
vars
  - Don't rely on color alone; WCAG AA contrast; forced-colors awareness

  **CSS authoring**
- Native CSS over compile-time SASS; keep `z()`, `lib/viewport`, `&`
nesting
  - Low specificity; avoid `!important` (comment when unavoidable)
  - `em`/`rem` + flexible sizing + `overflow-wrap`; `gap` over margins
- Local custom properties for reuse/`calc()`; mobile-first, intrinsic
layout
  - RTL via logical properties
  - Shared mixins (`ellipsis`, `line-clamp`, `d-animation`)
  - Style with restraint — minimal styling, leave aesthetics to themes

  **Accessibility**
  - Semantic/landmark markup; heading levels by outline
- Icon-only labels; `.sr-only` (not `display:none`); live regions via
the `a11y` service
- `:focus-visible`; `prefers-reduced-motion`; animate
`transform`/`opacity`
  - Design to work without hover

  **Templates**
- Escape by default (XSS); `dIcon` with valid sprite icons; translatable
strings
- `<DButton>` variants; FormKit for forms; `...attributes` on root;
`dConcatClass`
- `<PluginOutlet>` (don't add speculatively); avoid div-itis & empty
containers

  **Stylesheet placement**
- `common/` responsive stylesheets (`desktop/`/`mobile/` deprecated);
plugin/theme registration
  - Lint with `bin/lint --fix`
  
Also fixed an issue with two docs having the same index and cleaned up
some references to align better.
2026-06-01 11:08:46 -04:00

113 lines
4.2 KiB
Markdown
Vendored

# Layout & responsive reference
Companion to the **Where stylesheets live** section of `SKILL.md`. The rules live there:
write one responsive stylesheet (not desktop/mobile copies), prefer intrinsic layout, and use
`lib/viewport` for breakpoints. This file is the detail.
The authoritative philosophy is
[`docs/developer-guides/docs/03-code-internals/27-designing-for-devices.md`](../../../docs/developer-guides/docs/03-code-internals/27-designing-for-devices.md):
**design mobile-first, then enhance for larger viewports and richer input** — read it for the
full picture.
## Prefer intrinsic layout over breakpoints
Reach for a breakpoint only when a layout genuinely needs to *restructure*. For sizing and
wrapping, prefer **intrinsic, self-adjusting CSS** that responds to available space on its
own — it adapts at every width, not just at the breakpoints you happened to pick, and it's
far less code to maintain.
```scss
// GOOD — one rule, fills and wraps columns to fit any container width
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(14em, 1fr));
gap: 1em;
}
// AVOID — redefining the column count at each breakpoint
.card-grid {
grid-template-columns: 1fr;
@include viewport.from(sm) { grid-template-columns: repeat(2, 1fr); }
@include viewport.from(lg) { grid-template-columns: repeat(3, 1fr); }
@include viewport.from(xl) { grid-template-columns: repeat(4, 1fr); }
}
```
Other intrinsic tools to favor before media queries: `flex-wrap` with `flex` basis/grow,
`min()`/`max()`/`clamp()` for fluid sizing, `min-content`/`max-content`/`fit-content`, and
`auto-fit`/`auto-fill` + `minmax()`. Use `lib/viewport` breakpoints for the cases intrinsic
layout can't express — e.g. moving a sidebar from beside the content to below it, or swapping
`flex-direction`.
## Responsive breakpoints — `lib/viewport`
Make a component responsive with the standardized **`lib/viewport`** module rather than ad-hoc
media queries or the legacy `breakpoint()` mixin. `@use` it at the top of the file, then use
the `from` / `until` / `between` mixins:
```scss
@use "lib/viewport";
.my-component {
flex-direction: column; // mobile-first default
@include viewport.from(md) {
flex-direction: row; // wider viewports
}
&__sidebar {
@include viewport.until(lg) {
display: none;
}
}
}
```
Standard breakpoints
([`app/assets/stylesheets/lib/viewport.scss`](../../../app/assets/stylesheets/lib/viewport.scss)):
| Name | Width |
| --- | --- |
| `sm` | 40rem |
| `md` | 48rem |
| `lg` | 64rem |
| `xl` | 80rem |
| `2xl` | 96rem |
- `viewport.from($bp)``width >= $bp` (min-width); `viewport.until($bp)``width < $bp`
(max-width); `viewport.between($from, $until)` → a bounded range.
- Prefer a **mobile-first** default with `from()` to scale up. Always use a named breakpoint —
never a raw `px`/`rem` media query — so breakpoints stay consistent sitewide.
For viewport-conditional rendering in a component (rather than CSS), use the `capabilities`
service — `this.capabilities.viewport.lg`, etc. — but SCSS is the recommended way to handle
layout differences.
## Touch & hover
Some devices have only a touchscreen, some only a pointer, some both — and touch users **cannot
hover**. So design interfaces to **work entirely without hover**, and add hover only as an
enhancement. When you do add hover styling, scope it to non-touch devices via the
`html.discourse-no-touch` class (Discourse adds `.discourse-touch` / `.discourse-no-touch` to
`<html>` based on `(any-pointer: coarse)`):
```scss
html.discourse-no-touch .my-component__reveal-on-hover {
opacity: 0;
&:hover { opacity: 1; }
}
```
In components, the same info is on the `capabilities` service (`this.capabilities.touch`).
## Legacy device modes (deprecated)
Discourse historically shipped separate mobile/desktop stylesheets and layouts switched by
user-agent. **All of these are deprecated** and being removed — don't use them in new code:
- the `desktop/` and `mobile/` stylesheet directories,
- the `.mobile-view` / `.desktop-view` HTML classes,
- the `site.mobileView` boolean in JS.
Replace them with the viewport breakpoints and `capabilities` service above. ("Mobile mode"
will become an alias for "viewport width &lt; `sm`" for backwards compatibility.)