CSS pattern gotchas

Hard-won lessons about DaisyUI v5 + Tailwind v4 base resets, sub-pixel rendering, and CSS framework drift from v1.

Written for future agents (and future-Julian) so the same class of bug doesn't re-bite. Each entry: anti-pattern → failure mode → canonical fix.

Companion to CRDT-PATTERNS.md (communication/distributed-systems patterns). This file = visual/styling layer.


1. DaisyUI v5 sets border-width: 1px on bare <input>

Anti-pattern: Using border-b border-transparent (or border-l-2, border-t, etc.) and assuming the other three sides are 0. They are NOT — DaisyUI v5's base reset includes:

input:where(:not([type=button],[type=reset],[type=submit])) {
    border-width: 1px;
}

So border-b only sets border-bottom-width: 1px, leaving DaisyUI's 1px on the other three sides untouched. Net effect: +1px on each axis, ~+2px height when the input is inside a flex/border/padding container.

Failure mode: Section title input rendered 30px instead of 29px. ItemRow accumulated +2px vs v1. Zoom buttons rendered 25px instead of 24px. Checkbox border-width was 1.16234px vs 1px.

Canonical fix: explicit zero on the other sides:

<input class="border-b border-transparent border-x-0 border-t-0" />

Or border-0 first, then opt-in to specific sides:

<input class="border-0 border-b border-transparent" />

Discovered: 4 separate instances this sprint (zoom buttons, checkbox border, layout-zoom-overflow, section title input).

Composes with: §3 (sub-pixel rounding allow-list).


2. DaisyUI button classes carry implicit min-height

Anti-pattern: Using class="btn" or DaisyUI's component classes inside a tightly-sized container (e.g. a 24px row) and assuming Tailwind sizing classes override.

Failure mode: Zoom-in/out buttons rendered taller than v1's plain <button class="px-2 py-1 text-xs"> because DaisyUI's .btn defaults to a 4× line-height-relative min-height.

Canonical fix: explicit h-X clamp wins over DaisyUI defaults:

<button class="h-6 px-2 text-xs">−</button>

Discovered: mechanical sweep agent, commit 4874719a. Three CVL zoom controls.


3. Sub-pixel rounding diffs between v1 (React) and v2 (Svelte) renderers

Anti-pattern: Strict pixel-equality assertions in computed-style-diff for elements that hit fractional pixels via flex layouts or aspect-ratio.

Failure mode: v1 renders an input border at 1.16234px, v2 at 1px. v1 sub-pixel-rounded button width 382.4px, v2 382px. None are visual bugs; they're rendering-engine differences.

Canonical fix: allow-list intentional sub-pixel deltas in e2e/style-deltas-allowed.json. Don't try to "fix" them — they're not regressions.

Discovered: 5+ allow-listed entries by now. See the file for the catalog.


4. Flex child with overflow-auto grows with max-content inner div

Anti-pattern:

<div class="flex h-screen">
    <div class="cvl-preview-pane flex-1 overflow-auto">
        <div class="max-content">...zoomed Typst pages...</div>
    </div>
</div>

A flex child with overflow-auto will compute its scrollWidth to fit the max-content inner div, blowing past w-1/2 on parent.

Failure mode: layout-zoom-overflow.spec.ts failed across 5 viewports. Horizontal scrollbar appeared at zoom > 100%.

Canonical fix: split horizontal vs vertical overflow, add an inner scroll wrapper:

<div class="cvl-preview-pane flex-1 overflow-y-auto overflow-x-hidden">
    <div class="cvl-preview-scroll overflow-x-auto">
        <div class="max-content">...</div>
    </div>
</div>

Discovered: mechanical sweep agent, commit 4874719a.


5. Tailwind v4 line-height defaults differ from v3

(Placeholder — not yet hit, but worth catching when it does. Tailwind v4 changed default line-height on text-sm, text-xs etc. If a v1↔v2 height delta isn't explained by §1-4, this is the next suspect.)


Reference implementations

  • ui/src/lib/components/ui/Section.svelte — bottom-only border on section title input, with explicit border-x-0 border-t-0
  • ui/src/lib/components/cvl/CvGenerator.svelte — overflow split
    • inner scroll wrapper
  • ui/e2e/style-deltas-allowed.json — sub-pixel allow-list
Source: wiki/content/patterns/CSS-PATTERNS.md