<div class="docs-content" data-render>
{% raw %}
# Responsiveness
The interface is designed to work from a 320px phone to a wide desktop without a separate mobile site. The same HTML reflows; only the CSS changes per breakpoint.
## The goal
- **One readable column on small screens.** Multi-column layouts collapse to a single fluid column; secondary rails are dropped, not squeezed.
- **No accidental horizontal scroll.** Long content (code, URLs, tables) is contained, never pushing the page wider than the viewport.
- **Comfortable touch targets.** On touch devices every interactive element is at least `44 x 44px`.
- **No iOS zoom-on-focus.** Form controls are at least `16px` on touch devices so Safari does not zoom when they are focused.
- **Respect device safe areas.** Floating controls keep clear of notches and home indicators with `env(safe-area-inset-*)`.
The viewport is declared once in `base.html` and must not be changed:
```html
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
```
## The standard breakpoint ladder
Use these four breakpoints. They are `max-width` (desktop-first refinements), and they are the ones the shared shell already targets, so a page that uses them stays in step with the nav, breadcrumb, and footer.
| Breakpoint | What changes |
|-----------|--------------|
| `1024px` | Multi-column layouts collapse to one column; the right rail is hidden; the top nav links collapse into the hamburger menu; the page padding tightens. |
| `768px` | Horizontal tab strips (feed tabs, project tabs) switch to a single scrolling row; labels shrink. |
| `480px` | Compact paddings; secondary actions hidden; post/detail typography steps down; `grid-2col` becomes one column; floating buttons honour safe-area insets. |
| `360px` | Headers allowed to wrap; timestamps drop to their own line; the comment avatar is hidden so the input keeps room. |
> A handful of one-off breakpoints (960, 900, 760, 720, 640, …) exist in older stylesheets. They are exactly the inconsistency this section exists to remove. **Do not add new ad-hoc breakpoints**: reach for `1024 / 768 / 480 / 360` first, and only introduce another value if a specific component genuinely breaks between two of them, with a comment saying why.
## How it is achieved
**Collapse a grid to one column.** This is the whole mobile story for a multi-column page:
```css
@media (max-width: 1024px) {
.feed-layout { grid-template-columns: 1fr; }
.feed-right { display: none; }
}
```
**Keep the fluid column from overflowing.** A grid/flex child does not shrink below its content unless told to. Every fluid main column sets:
```css
.feed-main, .leaderboard-main { min-width: 0; }
```
**Let tab strips scroll instead of wrapping or overflowing:**
```css
@media (max-width: 768px) {
.feed-nav { overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch; }
.feed-nav-btn { flex-shrink: 0; }
}
```
**Guarantee touch targets and prevent zoom.** One shared rule in `base.css` covers every page, so individual pages do not repeat it:
```css
@media (hover: none) and (pointer: coarse) {
.btn, .topnav-link, .sidebar-link, .post-action-btn, .profile-tab { min-height: 44px; }
.topnav-icon, .emoji-toggle-btn { min-width: 44px; min-height: 44px; }
.modal-card input, .modal-card textarea, .modal-card select { font-size: 16px; }
}
```
**Keep floating controls off the notch / home bar:**
```css
@media (max-width: 480px) {
.feed-fab {
bottom: calc(1rem + env(safe-area-inset-bottom));
right: calc(1rem + env(safe-area-inset-right));
}
}
```
## Rules
1. **Use the four standard breakpoints** (`1024 / 768 / 480 / 360`); do not invent new ones without a written reason.
2. **Every fluid grid/flex column sets `min-width: 0`.** This single line prevents almost all horizontal-overflow bugs.
3. **Collapse, do not cram.** Below `1024px` a page is one fluid column; drop side rails with `display: none` rather than shrinking them to slivers.
4. **Never override the viewport meta or the shared touch-target rule.** They are global; rely on them.
5. **Test the small end.** The hard cases are `360px` (wrapping) and long unbroken strings inside a card.
{% endraw %}
</div>
<style>
.sg-resp { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.625rem; }
.sg-resp .sg-cell { border: 1px solid var(--border-light); border-radius: var(--radius); background: var(--bg-card); padding: 0.75rem; font-size: 0.8125rem; color: var(--text-secondary); text-align: center; }
.sg-resp-note { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.625rem; }
@media (max-width: 768px) {
.sg-resp { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
.sg-resp { grid-template-columns: 1fr; }
}
</style>
<div class="component-demo">
<div class="component-demo-title">Live reflow (three columns to two to one as the viewport narrows)</div>
<div class="sg-resp">
<div class="sg-cell">Item one</div>
<div class="sg-cell">Item two</div>
<div class="sg-cell">Item three</div>
<div class="sg-cell">Item four</div>
<div class="sg-cell">Item five</div>
<div class="sg-cell">Item six</div>
</div>
<p class="sg-resp-note">This grid uses the standard 768px and 480px breakpoints. Narrow the browser to watch it step from three columns to two to one.</p>
</div>