Refined UI

An opinionated, customizable superset of shadcn/ui

AgentsPrimitivesSkillsSkins
Guides
  • Overview
  • Get started
  • Shadcn compatibility
  • Architecture
  • Agent surface
CSS-first
  • CSS-first interactivity
  • CSS-first motion & sizing
React code quality
  • Derive, do not duplicate
  • Remount state boundaries
  • One responsibility per file
  • Real stack tests
UI / Tailwind
  • Use existing components first
  • Token discipline
  • Class Name boundaries
  • Tailwind v4 CSS configuration
  • Tailwind v4 migration syntax
  • Generated utilities before arbitrary values
  • Variant-first styling
  • Tailwind v4 capabilities
  • Tailwind v4 behavior & motion
UX
  • State continuity
  • Provenance without noise
  • Dense but scannable
  • Say it once
Refined UI registry
  • Skin authoring
  • Extension authoring
  • Registry-first DX
  • Skin completeness
  • Refined UI composition
  • Runtime-agnostic UI
  • Compound components DX
  • Context vs props
  1. Skills
  2. CSS-first motion & sizing
Skill / CSS-first

CSS-first motion & sizing

Drive sizing, enter/exit motion, and scroll effects from CSS and native attributes — token-driven and progressively enhanced — instead of measuring and animating values in JavaScript.

Goal

Move sizing and motion off the main thread and out of effects, keeping every duration on the theme's motion tokens and every cutting-edge property behind a working fallback.

Reach for modern CSS — relational selectors, container and style queries, and native platform elements — before React state, effects, and event handlers, so interaction, sizing, and motion stay declarative, server-rendered, and JavaScript-free.

Checks

1Auto-grow inputs with `field-sizing: content` (Tailwind `field-sizing-content`, bounded by `min-h`/`max-h`) instead of a ref copying `scrollHeight` back into a height; fade scroll edges with a `mask-image` gradient driven by overflow attributes, not a scroll listener.
2Animate enter/exit with `@starting-style` + `transition-behavior: allow-discrete` (Tailwind `starting:` + `transition-discrete`) rather than mount/unmount timers or an animation library, and keep every duration/easing on the theme's `--duration-*`/`--ease-*` tokens so the reduce-motion kill-switch flattens them.
3Animate to intrinsic size with the `grid-template-rows: 0fr → 1fr` collapse (widely supported) as the base, and progressively enhance to `interpolate-size: allow-keywords`/`calc-size(auto)` behind `@supports`, instead of measuring the panel and animating a pixel height.
4Guard Limited features — anchor positioning (`anchor()`/`position-try`), scroll-driven animations (`animation-timeline: scroll()`/`view()`), style queries, and `if()` — behind `@supports`, keeping the JS path (Floating UI, `IntersectionObserver`) as the fallback so an unsupported browser still gets a correct, static result.

Avoid

1Measuring `scrollHeight`/`getBoundingClientRect` on every keystroke, resize, or scroll to set a style a container query, `field-sizing`, or scroll-driven animation already reaches.
2JS enter/exit libraries for a fade or slide that `@starting-style` plus a token transition expresses in a few lines.
3Shipping an experimental CSS feature (anchor positioning, `if()`, `interpolate-size`, scroll-driven timelines) as a hard dependency with no `@supports` fallback, or hardcoding millisecond durations that bypass the motion tokens and the reduce-motion kill-switch.

On this page

  • Checks
  • Avoid