$npx -y skills add dembrandt/dembrandt-skills --skill sticky-and-fixed-elementsSticky and fixed positioning keeps critical UI persistent as the user scrolls — headers at the top, toolbars at the bottom on mobile. Use deliberately: too many fixed layers create visual noise and reduce content area. Use when designing navigation headers, bottom toolbars, float
| 1 | # Sticky and Fixed Elements |
| 2 | |
| 3 | ## position: fixed vs position: sticky |
| 4 | |
| 5 | | Property | Behaviour | Use for | |
| 6 | |---|---|---| |
| 7 | | `position: fixed` | Removed from document flow. Always stays at the same viewport position regardless of scroll or parent. | Global navigation header, bottom toolbar, floating action button | |
| 8 | | `position: sticky` | Stays in document flow until it hits its scroll threshold, then locks in place. Returns to flow when parent scrolls past. | Table column headers, section headings in a long list, in-page toolbars within a scroll container | |
| 9 | |
| 10 | **Prefer `sticky` over `fixed`** when the element belongs to a specific section or scroll context. Fixed elements sit above everything and affect the entire viewport — use them only for truly global UI. |
| 11 | |
| 12 | ## Top — Navigation Header |
| 13 | |
| 14 | The global navigation header is the most common fixed element. It should: |
| 15 | |
| 16 | - Remain visible at all times so the user can always navigate away |
| 17 | - Be as thin as possible to maximise content area — 48–64px is a common range |
| 18 | - Have a background and shadow so content scrolling beneath it does not bleed through |
| 19 | - On scroll, a subtle shadow (`--shadow-sm`) signals that content is behind it |
| 20 | |
| 21 | ```css |
| 22 | .site-header { |
| 23 | position: fixed; |
| 24 | top: 0; |
| 25 | left: 0; |
| 26 | right: 0; |
| 27 | height: var(--header-height); |
| 28 | background: var(--color-surface); |
| 29 | box-shadow: var(--shadow-sm); |
| 30 | z-index: var(--z-header); |
| 31 | } |
| 32 | ``` |
| 33 | |
| 34 | Compensate for the fixed header with matching top padding on the page body: |
| 35 | ```css |
| 36 | body { padding-top: var(--header-height); } |
| 37 | ``` |
| 38 | |
| 39 | ## Bottom — Toolbar on Mobile |
| 40 | |
| 41 | On mobile, the bottom of the screen is the most reachable area with one thumb. A persistent bottom toolbar is the natural home for: |
| 42 | |
| 43 | - Primary navigation (bottom tab bar — iOS and Android convention) |
| 44 | - Contextual actions for the current view (edit, share, delete) |
| 45 | - A floating action button (FAB) for the single most important action |
| 46 | |
| 47 | Bottom toolbars should: |
| 48 | - Respect the safe area inset on devices with a home indicator: `padding-bottom: env(safe-area-inset-bottom)` |
| 49 | - Be visually separated from content (background fill, top border, or shadow) |
| 50 | - Contain 3–5 items maximum — more than 5 should move to a menu or a different pattern |
| 51 | - Use icons with labels for navigation tabs, or icons alone for contextual toolbars (with tooltips) |
| 52 | |
| 53 | On desktop, bottom toolbars are uncommon. Status bars, editor toolbars, and command palettes are the desktop equivalent — these are typically `position: fixed` at the bottom or `position: sticky` within their scroll container. |
| 54 | |
| 55 | ## Sticky Table Headers |
| 56 | |
| 57 | For data tables inside a scroll container, sticky column headers prevent the user from losing track of what each column means. |
| 58 | |
| 59 | ```css |
| 60 | thead th { |
| 61 | position: sticky; |
| 62 | top: 0; |
| 63 | background: var(--color-surface); |
| 64 | z-index: 1; |
| 65 | } |
| 66 | ``` |
| 67 | |
| 68 | If the table also has a sticky first column (for row identifiers), the top-left cell needs both `top: 0` and `left: 0`, and a higher `z-index` to sit above both the header row and the sticky column. |
| 69 | |
| 70 | ## Stacking and Z-index |
| 71 | |
| 72 | Sticky and fixed elements create stacking context. Define z-index as named tokens, not arbitrary numbers: |
| 73 | |
| 74 | ```css |
| 75 | --z-base: 0; |
| 76 | --z-dropdown: 100; |
| 77 | --z-sticky: 200; |
| 78 | --z-header: 300; |
| 79 | --z-modal: 400; |
| 80 | --z-toast: 500; |
| 81 | ``` |
| 82 | |
| 83 | Every fixed or sticky element must declare its z-index explicitly using a token. Avoid ad-hoc values like `z-index: 9999` — they signal an unmanaged stacking context and will eventually conflict. |
| 84 | |
| 85 | ## How Many Fixed Layers Is Too Many |
| 86 | |
| 87 | Each fixed layer removes space from the content area and adds visual complexity. A page should rarely need more than: |
| 88 | |
| 89 | - 1 fixed header (top) |
| 90 | - 1 fixed toolbar or bottom nav (bottom, mobile) |
| 91 | - 1 floating action button or contextual overlay |
| 92 | |
| 93 | A fixed header + fixed bottom too |