Skip to main content

css-styling

Style components with the CSS engine — ClassBuilder DSL, Styleable mixin, utility classes, design tokens, and component CSS

Jump to install

Source facts

Repository
amberframework/asset_pipeline
Last source activity
February 18, 2026 at 11:55
Detected SKILL.md language
English
Stars
5
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
css-styling
description
Style components with the CSS engine — ClassBuilder DSL, Styleable mixin, utility classes, design tokens, and component CSS
user-invocable
true
# CSS Styling with the Asset Pipeline You are a CSS specialist who styles Crystal components using the asset pipeline's utility-first CSS engine. The engine generates CSS from class names, supports responsive breakpoints, pseudo-states, ARIA states, dark mode, and WCAG 2.2 AA accessibility defaults. Colors use the OKLCH color space. ## ClassBuilder DSL Build CSS class strings with a fluent API. Classes are automatically registered with the ClassRegistry for CSS generation. ```crystal classes = Components::CSS::ClassBuilder.new .base("flex", "items-center", "gap-4") .hover("shadow-lg") .focus_visible("ring-2", "ring-blue-500") .dark("bg-gray-800", "text-gray-100") .responsive { |r| r.sm("flex-col") r.lg("flex-row") } .build # => "flex items-center gap-4 hover:shadow-lg focus-visible:ring-2 focus-visible:ring-blue-500 dark:bg-gray-800 dark:text-gray-100 sm:flex-col lg:flex-row" ``` ### All ClassBuilder Methods | Method | Purpose | CSS Output | |--------|---------|------------| | `.base(*classes)` | Foundation classes | Classes as-is | | `.add(classes, condition?)` | Conditional add | Classes if condition true | | `.when(condition, classes)` | Add if true | Classes if condition true | | `.unless(condition, classes)` | Add if false | Classes if condition false | | `.hover(classes)` | Mouse hover | `hover:` prefix | | `.focus(classes)` | Any focus | `focus:` prefix | | `.active(classes)` | Active/pressed | `active:` prefix | | `.disabled(classes)` | Disabled state | `disabled:` prefix | | `.dark(classes)` | Dark color scheme | `dark:` prefix | | `.focus_visible(classes)` | Keyboard focus (WCAG 2.4.7) | `focus-visible:` prefix | | `.focus_within(classes)` | Child has focus | `focus-within:` prefix | | `.motion_safe(classes)` | No motion preference (WCAG 2.2.2) | `motion-safe:` prefix | | `.motion_reduce(classes)` | Prefers reduced motion | `motion-reduce:` prefix | | `.aria_expanded(classes)` | aria-expanded="true" | `aria-expanded:` prefix | | `.aria_selected(classes)` | aria-selected="true" | `aria-selected:` prefix | | `.aria_checked(classes)` | aria-checked="true" | `aria-checked:` prefix | | `.aria_disabled(classes)` | aria-disabled="true" | `aria-disabled:` prefix | | `.invalid(classes)` | Form invalid state | `invalid:` prefix | | `.valid(classes)` | Form valid state | `valid:` prefix | | `.responsive { \|r\| ... }` | Breakpoint variants | `sm:`, `md:`, `lg:`, `xl:`, `xxl:` | | `.build` | Returns deduped class string | Registers with ClassRegistry | ### ResponsiveBuilder Inside the `.responsive` block: ```crystal .responsive { |r| r.sm("text-sm") # >= 640px r.md("text-base") # >= 768px r.lg("text-lg") # >= 1024px r.xl("text-xl") # >= 1280px r.xxl("text-2xl") # >= 1536px } ``` --- ## Styleable Mixin Include `Components::CSS::Styleable` in components for convenience methods. ### `css { |c| ... }` Shorthand for ClassBuilder — creates builder, yields it, calls `.build`: ```crystal class MyComponent < Components::StatelessComponent include Components::CSS::Styleable def render_content : String wrapper_classes = css { |c| c.base("card", "rounded-lg", "shadow-md") c.hover("shadow-xl") c.dark("bg-gray-800") } Components::Elements::Div.new(class: wrapper_classes).build { |d| d << "Content" }.render end end ``` ### `class_names(**options)` Conditional class helper — includes class when value is truthy: ```crystal class_names( "btn" => true, "btn-primary" => variant == "primary", "btn-disabled" => disabled?, "btn-lg" => size == "large" ) # => "btn btn-primary" (if variant is "primary" and not disabled/large) ``` Values can be `String` (always included), `Bool`, or `Array(String)`. ### `variant_classes(base, variant?, size?, state?)` Generates variant class strings: ```crystal variant_classes("btn", "primary", "lg") # => "btn btn-primary btn-lg" variant_classes("alert", "success", nil, "dismissible") # => "alert alert-success alert-dismissible" ``` ### `merge_classes(*class_strings)` Merge and deduplicate multiple class strings: ```crystal merge_classes("flex gap-4", "flex items-center gap-8") # => "flex gap-4 items-center gap-8" ``` --- ## Design Tokens Configured via `Components::CSS::Config`. Default values below. ### Colors (OKLCH) Four color families, each with shades 50-950: | Token | 50 (lightest) | 500 (base) | 950 (darkest) | |-------|---------------|------------|---------------| | `gray` | `oklch(0.985 0 0)` | `oklch(0.551 0 0)` | `oklch(0.13 0 0)` | | `red` | `oklch(0.971 0.013 17.38)` | `oklch(0.637 0.237 25.331)` | `oklch(0.258 0.092 26.042)` | | `blue` | `oklch(0.97 0.014 254.604)` | `oklch(0.623 0.214 259.815)` | `oklch(0.282 0.091 267.935)` | | `green` | `oklch(0.982 0.018 155.826)` | `oklch(0.723 0.219 149.579)` | `oklch(0.266 0.065 152.934)` | Full shade scale: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950 Usage in utility classes: `bg-blue-500`, `text-gray-900`, `border-red-300` ### Spacing | Token | Value | Token | Value | |-------|-------|-------|-------| | `0` | `0` | `8` | `2rem` | | `px` | `1px` | `10` | `2.5rem` | | `0.5` | `0.125rem` | `12` | `3rem` | | `1` | `0.25rem` | `16` | `4rem` | | `2` | `0.5rem` | `20` | `5rem` | | `3` | `0.75rem` | `24` | `6rem` | | `4` | `1rem` | `32` | `8rem` | | `5` | `1.25rem` | `48` | `12rem` | | `6` | `1.5rem` | `64` | `16rem` | | `7` | `1.75rem` | `96` | `24rem` | Usage: `p-4` (1rem padding), `mt-8` (2rem margin-top), `gap-2` (0.5rem gap) ### Breakpoints | Name | Min-width | Usage | |------|-----------|-------| | `sm` | `640px` | `sm:flex` | | `md` | `768px` | `md:grid-cols-2` | | `lg` | `1024px` | `lg:text-lg` | | `xl` | `1280px` | `xl:max-w-screen-xl` | | `2xl` | `1536px` | `2xl:px-8` | ### Font Sizes | Token | Value | Token | Value | |-------|-------|-------|-------| | `xs` | `0.75rem` | `xl` | `1.25rem` | | `sm` | `0.875rem` | `2xl` | `1.5rem` | | `base` | `1rem` | `3xl` | `1.875rem` | | `lg` | `1.125rem` | | | ### Border Radius | Token | Value | |-------|-------| | `none` | `0` | | `sm` | `0.125rem` | | `(default)` | `0.25rem` | | `md` | `0.375rem` | | `lg` | `0.5rem` | | `xl` | `0.75rem` | | `2xl` | `1rem` | | `3xl` | `1.5rem` | | `full` | `9999px` | ### Shadows | Token | Description | |-------|-------------| | `sm` | Small shadow | | `(default)` | Medium shadow | | `md` | Medium-large shadow | | `lg` | Large shadow | | `xl` | Extra-large shadow | --- ## Utility Classes The CSS parser (`Components::CSS::Engine::Parser`) handles 100+ utility patterns. ### Layout & Display `block`, `inline-block`, `inline`, `flex`, `inline-flex`, `grid`, `hidden` ### Position `relative`, `absolute`, `fixed`, `sticky` ### Flexbox `flex-row`, `flex-col`, `flex-wrap`, `flex-nowrap`, `items-start`, `items-center`, `items-end`, `items-stretch`, `justify-start`, `justify-center`, `justify-end`, `justify-between`, `justify-around`, `gap-{size}` ### Spacing Margin: `m-{size}`, `mx-{size}`, `my-{size}`, `mt-{size}`, `mr-{size}`, `mb-{size}`, `ml-{size}` Padding: `p-{size}`, `px-{size}`, `py-{size}`, `pt-{size}`, `pr-{size}`, `pb-{size}`, `pl-{size}` ### Sizing
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub