29 KiB
name: kelp-ui description: Frontend development with Kelp UI (v1.17.2): an HTML-first UI library using HUG CSS, light-DOM web components, and cascade layers with no build step. Complete component, layout, and utility reference with HTML examples, progressive enhancement patterns, and anti-patterns to prevent framework-style mistakes
Role
Act as a Senior Frontend Developer specializing in Kelp UI. Generate, review, and guide HTML implementation using Kelp's HTML-first approach. Always produce semantic, progressively enhanced HTML that follows Kelp's HUG CSS methodology — classless first, utilities to nudge, group classes for complex components.
When to Use
- Building UI with Kelp UI library
- Reviewing existing HTML for Kelp best practices and anti-patterns
- Choosing between similar Kelp components or layouts
- Customizing a Kelp theme (CSS variables, cascade layers)
- Debugging Kelp component behavior
- Implementing progressive enhancement patterns with Kelp web components
Version Awareness
This skill was built against Kelp UI v1.17.2.
- When uncertain about a feature, attribute, or component, use WebFetch to check
https://kelpui.com/docsfor the current documentation - Flag when a recommendation might be version-dependent
- The source of truth is always
kelpui.com/docs, not any local repository
Core Principles
These 6 rules govern every decision when working with Kelp. Violating any of them is an anti-pattern.
1. HTML First (HUG CSS)
Kelp uses a HTML > Utility > Group approach:
- Classless HTML for core styles — bare
<h1>,<p>,<button>,<input>are styled without any classes - Utility classes to nudge and tweak when needed —
.secondary,.subtle,.size-l - Group classes for complex components —
.callout,.split,.grid
<!-- Good: classless HTML, styled by default -->
<h1>Hello world!</h1>
<p>How are you today?</p>
<button>Say Hi</button>
<!-- Good: utility classes to nudge -->
<button class="secondary subtle">Say Hi</button>
2. Progressive Enhancement
Web components enhance content that is already usable without JavaScript.
<!-- Before JS loads: user sees "1295" -->
<!-- After JS loads: user sees "$1,295" -->
This costs
<format-number type="currency">
1295
</format-number>.
Content must always be accessible before JavaScript runs.
3. Light DOM Only
Kelp web components use the light DOM exclusively. No Shadow DOM, ever. This means standard CSS applies, no style encapsulation barriers, and the DOM is inspectable.
4. No Build Step Required
Kelp uses modern HTML, CSS, and JavaScript natively. Everything is customizable with CSS variables and HTML attributes. No Vite, Webpack, Rollup, or any bundler required.
You can use a build step if the project already has one, but you never need one.
5. Cascade Layers for Customization
Kelp uses CSS @layer to control cascade order:
kelp— parent layer. Code outside this layer takes priority automatically.kelp.theme— customize colors, fonts, sizes, breakpointskelp.extend— add new features, customize existing componentskelp.helpers— add or customize utility classeskelp.effects— add or override state-based effects (:hover,:active)
CSS outside the kelp layer always wins, making overrides simple without specificity battles.
6. Semantic HTML is the Component
Use native HTML elements first. A <nav> is a nav, a <button> is a button, a <dialog> is a dialog. Never wrap semantic elements in meaningless <div>s or recreate native behavior with JavaScript.
CSS Architecture
Loading
Always use <link> tags. Never @import.
<!-- CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/kelpui@1/css/kelp.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/kelpui@1/js/kelp.js"></script>
<!-- Local -->
<link rel="stylesheet" href="./css/kelp.css">
<script type="module" src="./js/kelp.js"></script>
Starter HTML
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page Title</title>
<link rel="stylesheet" href="./css/kelp.css">
</head>
<body>
<main class="container">
<h1>Hello World</h1>
</main>
</body>
<script type="module" src="./js/kelp.js"></script>
</html>
Cascade Layers
kelp (parent — everything inside)
├── kelp.theme — colors, fonts, sizes, breakpoints
├── kelp.extend — new features, component customization
├── kelp.helpers — utility class customization
└── kelp.effects — state-based effects (:hover, :active)
Code outside the kelp layer overrides everything inside it.
Do not use internal Kelp layers — they can change without notice.
Custom Theme Pattern
Create a kelp.custom.css file and wrap overrides in the appropriate layer:
@layer kelp.theme {
:root {
--font-primary: "Source Sans Pro", sans-serif;
--font-secondary: "Playfair Display", serif;
--font-size-base: 112.5%;
}
}
CSS Variables Reference
Fonts:
| Variable | Default | Usage |
|---|---|---|
--font-primary |
Sans serif stack | Body text, most elements |
--font-secondary |
Serif stack | Headings |
--font-monospace |
Monospace stack | Code snippets |
Breakpoints:
| Variable | Size |
|---|---|
--breakpoint-xs |
18em |
--breakpoint-s |
28em |
--breakpoint-m |
38em |
--breakpoint-l |
52em |
--breakpoint-xl |
60em |
--breakpoint-2xl |
80em |
Note: CSS variables cannot be used in @media queries. These are used for element sizing (e.g., container-* max-widths).
Size Scale (em-based, scales with font-size):
| Variable | Size | px Equiv |
|---|---|---|
--size-6xs |
0.125em |
2px |
--size-5xs |
0.25em |
4px |
--size-4xs |
0.5em |
8px |
--size-3xs |
0.6875em |
11px |
--size-2xs |
0.75em |
12px |
--size-xs |
0.8125em |
13px |
--size-s |
0.9375em |
15px |
--size-m |
1em |
16px |
--size-l |
1.0625em |
17px |
--size-xl |
1.1875em |
19px |
--size-2xl |
1.3125em |
21px |
--size-3xl |
1.5em |
24px |
--size-4xl |
1.75em |
28px |
--size-5xl |
2em |
32px |
--size-6xl |
3em |
48px |
--space = 1.5em (matches default line-height, used for margins and padding).
--font-size-base = 112.5% (18px default). Changing this scales the entire UI.
Border Radius:
| Variable | Size |
|---|---|
--border-radius-s |
0.25em |
--border-radius-m |
0.5em |
--border-radius-l |
1.3125em |
--border-radius-circle |
50% |
Line Heights:
| Variable | Value |
|---|---|
--line-height-xs |
1.2 |
--line-height-s |
1.4 |
--line-height-m |
1.5 (default) |
Component Color Variables (used by all components):
| Variable | Purpose |
|---|---|
--background-color |
Background (+ -hover, -active) |
--border-color |
Border (+ -hover, -active) |
--color |
Text color (+ -hover, -active) |
These use semantic color variables (--color-fill-muted, --color-on-muted, etc.) as values. Utility classes like .primary, .secondary, .success, .danger adjust them.
Focus Ring:
| Variable | Default |
|---|---|
--focus-ring-color |
--color-blue-50 (light) / --color-blue-60 (dark) |
--focus-ring-style |
solid |
--focus-ring-width |
--size-6xs |
--focus-ring-offset |
--size-6xs |
Kelp uses :focus-visible — focus rings appear only on keyboard navigation.
Dark Mode
Class-based, not media-query-based. This is intentional — it provides more flexibility.
<!-- Entire site dark -->
<body class="dark">...</body>
<!-- Section overrides -->
<body class="dark">
<div class="light">This section is light</div>
</body>
<!-- No class = light by default -->
<body>...</body>
Auto-detect from OS settings — load this script in <head>:
<script src="https://cdn.jsdelivr.net/npm/kelpui@1/js/dark-mode-auto.js"></script>
Layout Reference
Choosing a Layout
| Need | Layout | Class |
|---|---|---|
| Two items pushed to opposite edges | Split | .split |
| One fixed-width + one fluid item | Sidecar | .sidecar / .sidecar-end |
| Multi-column with explicit proportions | Grid | .grid + .item-* |
| Equal-width auto-columns | Grid auto | .grid-auto |
| Horizontal wrapping list of items | Cluster | .cluster |
| Vertical stack with consistent spacing | Stack | .stack |
| Constrain content width | Container | .container-* |
| Readable text column | Text | .text |
| Title + actions in a row | Action Header | .action-header |
Container
Constrains content to a max-width. Uses breakpoint variables.
<div class="container">Full width up to default max</div>
<div class="container-s">Small container (28em)</div>
<div class="container-m">Medium container (38em)</div>
<div class="container-l">Large container (52em)</div>
<div class="container-xl">Extra large container (60em)</div>
Grid
12-column CSS Grid with fraction-based sizing.
<!-- Two-column layout -->
<div class="grid">
<div class="item-third">Sidebar (4/12)</div>
<div class="item-two-thirds">Main content (8/12)</div>
</div>
<!-- Responsive: stack on mobile, columns on medium+ -->
<div class="grid grid-m">
<div class="item-half">Left</div>
<div class="item-half">Right</div>
</div>
<!-- Auto-grid: equal-width columns based on --width -->
<div class="grid-auto">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
</div>
Column sizes: .item-fourth (3/12), .item-third (4/12), .item-half (6/12), .item-two-thirds (8/12), .item-three-fourths (9/12).
Responsive breakpoints: .grid-s (28em), .grid-m (32em), .grid-l (52em), .grid-xl (60em).
Content choreography: .start-first, .start-third, .start-two-thirds for reordering.
Cluster
Horizontal wrapping group with consistent gap.
<div class="cluster">
<span>Tag 1</span>
<span>Tag 2</span>
<span>Tag 3</span>
</div>
Split
Push items to opposite edges.
<!-- Horizontal: logo left, nav right -->
<div class="split">
<div>Logo</div>
<nav>Links</nav>
</div>
<!-- Vertical split -->
<div class="split direction-column">
<div>Top</div>
<div>Bottom</div>
</div>
Items keep their natural width. Use for navbars, header rows, footer bars.
Sidecar
One item fixed-width, the other fills remaining space.
<!-- First item fixed, second fluid -->
<div class="sidecar">
<img src="avatar.jpg" alt="Avatar">
<div>Content fills remaining space</div>
</div>
<!-- Second item fixed, first fluid -->
<div class="sidecar-end">
<div>Content fills remaining space</div>
<button>Action</button>
</div>
Use for: icon + text, avatar + content, search field + button, sidebar + main.
Split vs Sidecar: Split pushes items apart with space between (both keep natural width). Sidecar makes one item grow to fill space (asymmetric).
Stack
Vertical stack with consistent spacing between children.
<div class="stack">
<h2>Section Title</h2>
<p>Content paragraph.</p>
<button>Action</button>
</div>
Text
Sets optimal line length for readable text.
<div class="text">
<p>Long paragraph of readable content...</p>
</div>
Action Header
Title + actions in a row, with title taking priority.
<div class="action-header">
<h2>Page Title</h2>
<div class="cluster">
<button>Edit</button>
<button class="danger">Delete</button>
</div>
</div>
Component Reference
Typography
Headings — classless <h1> through <h6>. Styled automatically. Never add heading classes.
Text — classless <p>, <strong>, <em>, <mark>, <small>, <del>, <ins>, <sub>, <sup>. All styled.
Links — classless <a>. Styled with dotted underline by default.
Blockquotes — classless <blockquote>. Use <cite> for attribution.
Code — classless <code> for inline, <pre><code> for blocks.
Lists — classless <ul>, <ol>, <dl>. Nested lists supported.
Dividers — classless <hr>.
Content
Badges — <span class="badge">. Modifiers: .primary, .secondary, .success, .danger, .warning.
Avatar — <img class="avatar">. Modifiers: .size-s, .size-l, .size-xl.
Callouts — <div class="callout">. Modifiers: .primary, .secondary, .success, .danger, .warning.
<div class="callout warning">
<p><strong>Warning:</strong> This is a callout.</p>
</div>
Skeleton — <div class="skeleton"> for loading placeholders.
Spinner — <span class="spinner" role="status"><span class="visually-hidden">Loading...</span></span>.
Tables — classless <table>. Kelp styles all tables automatically.
Media — classless <img>, <video>, <audio>. Responsive by default.
Navigation
Navbar — <nav class="navbar">. Use with .split for layout.
<header>
<nav class="navbar split">
<a href="/">Logo</a>
<div class="cluster">
<a href="/about">About</a>
<a href="/contact">Contact</a>
</div>
</nav>
</header>
Subnav — <kelp-subnav> web component. Horizontally scrollable navigation with overflow handling.
Collapse Menu — responsive nav that collapses into a toggle button on small screens.
Tabs — <kelp-tabs> web component. Progressive enhancement — without JS, content is a list of linked sections.
<kelp-tabs>
<ul>
<li><a href="#tab1">Tab 1</a></li>
<li><a href="#tab2">Tab 2</a></li>
</ul>
<div id="tab1">Tab 1 content</div>
<div id="tab2">Tab 2 content</div>
</kelp-tabs>
Forms
All form elements are classless — <input>, <textarea>, <select>, <label>, <fieldset>, <legend> are styled automatically.
<!-- Good: no classes needed -->
<label for="email">Email</label>
<input type="email" id="email" name="email">
<!-- Good: utility class to nudge -->
<button class="primary">Submit</button>
Checkbox — classless <input type="checkbox">.
Radio — classless <input type="radio">.
Switch — <input type="checkbox" role="switch">.
Password — <kelp-toggle-pw> wraps password field to add show/hide toggle.
<kelp-toggle-pw>
<label for="pw">Password</label>
<input type="password" id="pw" name="pw">
</kelp-toggle-pw>
Color Picker — classless <input type="color">.
Date/Time Picker — classless <input type="date">, <input type="time">, <input type="datetime-local">.
File Picker — classless <input type="file">.
Select All — <kelp-select-all> wrapping a group of checkboxes.
Form Validation — <kelp-form-validate> wraps a <form> for enhanced client-side validation with custom messages.
Dialog / Overlay
All overlays use the native <dialog> element. Never build custom overlay components.
Modal — <dialog> with no extra class. Centered in viewport.
<button commandfor="my-modal" command="show-modal">Open</button>
<dialog id="my-modal">
<p>Modal content</p>
<button commandfor="my-modal" command="close">Close</button>
</dialog>
Drawer — <dialog class="drawer">. Edge-aligned panel.
<button commandfor="my-drawer" command="show-modal">Open</button>
<dialog class="drawer" id="my-drawer">
<p>Drawer content</p>
<button commandfor="my-drawer" command="close">Close</button>
</dialog>
Drawer positions: .drawer (end/right), .drawer-start (start/left), .drawer-top, .drawer-bottom.
Dialog attributes:
closedby="any"— click backdrop to close (light dismiss)closedby="none"— prevent all dismissal except explicit.close()autofocuson an inner element — set initial focus
Dialog sizing: .dialog-xs, .dialog-s, .dialog-l, .dialog-xl, .dialog-full.
Sticky header/footer: Use .action-header, .action-body, .action-footer inside dialog for scrollable body with sticky header/footer.
Events: cancel, close, command — note: these do not bubble. Use {capture: true} if listening on a parent.
Kelp includes a polyfill for the [command]/[commandfor] attributes.
Choosing Between Similar Components
Expandable content:
| Need | Component | JS Required |
|---|---|---|
| Static FAQ, accordion, inline expand/collapse | <details> + <summary> |
No |
| Button-triggered show/hide with ARIA + events | <kelp-disclosure> |
Yes |
| Dropdown menu | <kelp-disclosure is-dropdown> |
Yes |
<details> uses native HTML, pushes content inline. <kelp-disclosure> adds full ARIA pattern, cancelable events, and can target any element by ID. Dropdown is just a disclosure with [is-dropdown] — auto-closes on outside click or Escape.
Accordion: Use multiple <details> elements with the same [name] attribute — the browser enforces only one open at a time.
<details name="faq">
<summary>Question 1</summary>
<p>Answer 1</p>
</details>
<details name="faq">
<summary>Question 2</summary>
<p>Answer 2</p>
</details>
Overlays:
| Need | Pattern | Class |
|---|---|---|
| Centered dialog/modal | <dialog> |
(none) |
| Side panel / drawer | <dialog> |
.drawer |
| All overlays | <dialog> |
Native element — never custom |
Ajax:
| Need | Component |
|---|---|
| Submit form without page reload | <kelp-form-ajax> wrapping <form> |
| Update page content after an event | <kelp-html-ajax> |
These pair together: form-ajax submits and emits events, html-ajax listens and updates content.
Details Disclosure
Native HTML expand/collapse. No JavaScript required.
<details>
<summary>Click to expand</summary>
<p>Hidden content here.</p>
</details>
Attributes: [open] (expanded by default), [name] (exclusive accordion groups).
Disclosure (Web Component)
<kelp-disclosure> — button-triggered show/hide with ARIA.
<kelp-disclosure target="#content">
<button>Toggle</button>
</kelp-disclosure>
<div id="content">Content to show/hide</div>
Attributes: [target] (ID selector), [is-dropdown], [hide-until-ready], [show-until-ready].
Events: kelp-disclosure:ready, kelp-disclosure:show-before (cancelable), kelp-disclosure:show, kelp-disclosure:hide-before (cancelable), kelp-disclosure:hide.
Methods: .show(), .hide(), .toggle(), .init().
Dropdown
Alias of Disclosure with [is-dropdown]. Menu must be a <ul> nested inside.
<kelp-disclosure is-dropdown>
<button>Menu</button>
<ul>
<li><a href="#">Link</a></li>
<li><button>Action</button></li>
</ul>
</kelp-disclosure>
Modifier: .dropdown-end on <ul> for right-aligned menu.
Ajax Components
Form Ajax — wraps a <form> for async submission:
<kelp-form-ajax msg-submitting="Sending..." msg-success="Done!" msg-failed="Error">
<form action="/api/submit" method="POST">
<label for="name">Name</label>
<input type="text" id="name" name="name">
<button>Submit</button>
</form>
</kelp-form-ajax>
Key attributes: [msg-submitting], [msg-success], [msg-failed], [redirect-on-success], [path-on-success], [path-on-failed], [submit-loading], [keep-fields], [remove-form-on-success], [dismiss-msg-on-success], [delay], [external-forms], [event-keys], [msg-start].
Events: kelp-form-ajax:ready, kelp-form-ajax:submit (cancelable), kelp-form-ajax:success, kelp-form-ajax:failed.
Progressive enhancement: the form still submits normally without JS.
HTML Ajax — listens for events and replaces its own content:
<kelp-html-ajax id="item-list" events="kelp-form-ajax:success">
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
</kelp-html-ajax>
Attributes: [events] (comma-separated event names), [keys] (filter by event keys).
Events: kelp-html-ajax:ready, kelp-html-ajax:before-replace (cancelable), kelp-html-ajax:replace, kelp-html-ajax:remove, kelp-html-ajax:failed.
Other Web Components
kelp-tabs — tabbed interface. Content is linked sections, progressively enhanced into tabs.
kelp-toc — auto-generated table of contents from headings.
kelp-autoexpand — wraps <textarea> to auto-grow with content.
kelp-toggle-pw — wraps password input to add show/hide toggle.
kelp-heading-anchors — adds anchor links to headings.
kelp-subnav — horizontally scrollable navigation.
kelp-form-validate — enhanced client-side form validation.
All web components follow the kelp-*:event naming convention for events. All support progressive enhancement — content is usable before JS loads.
Utility Classes Reference
17 categories. Size modifiers follow the same scale as CSS variables (-6xs through -6xl).
| Category | Classes | Purpose |
|---|---|---|
| Align Items | .align-start, .align-center, .align-end |
Flex cross-axis alignment |
| Aspect Ratio | .aspect-ratio-* |
Fixed aspect ratios |
| Color & Style | .primary, .secondary, .success, .danger, .warning, .muted, .subtle, .filled |
Component color and fill |
| Display | .block, .inline, .inline-block, .flex, .inline-flex, .grid, .none |
Display mode |
| Divided | .divided |
Visual dividers between children |
| Fill | .fill, .fill-* |
Background fills |
| Flexbox | .flex-wrap, .flex-nowrap, .flex-grow, .flex-shrink, .direction-column, .direction-row |
Flex behavior |
| Gap | .gap-* |
Space between flex/grid children |
| Hide Above | .hide-above-* |
Hide above breakpoint |
| Justify Content | .justify-start, .justify-center, .justify-end, .justify-between, .justify-around |
Flex main-axis alignment |
| Margin | .margin-*, .margin-top-*, .margin-bottom-*, etc. |
Spacing outside |
| Motion | .motion-reduced |
Disable animations |
| Padding | .padding-*, .padding-top-*, .padding-bottom-*, etc. |
Spacing inside |
| Show Above | .show-above-* |
Show above breakpoint |
| Size | .size-* |
Font size (scales entire element via em) |
| Text | .text-center, .text-left, .text-right, .text-uppercase, .text-lowercase, .text-capitalize, .text-truncate, .text-nowrap |
Text formatting |
| Visibility | .visually-hidden, .visible |
Accessibility visibility |
Output Format
When generating UI code
- Show complete, copy-pasteable HTML — not fragments that require assembly
- Include CSS/JS link tags at the top if the component requires specific Kelp files
- Annotate with comments explaining which Kelp pattern is used:
<!-- kelp: split layout -->
<div class="split">
<div>Left</div>
<div>Right</div>
</div>
- For web components, show the progressive enhancement baseline first (what works without JS), then the enhanced version:
<!-- Without JS: linked sections -->
<ul>
<li><a href="#tab1">Tab 1</a></li>
<li><a href="#tab2">Tab 2</a></li>
</ul>
<div id="tab1">Tab 1 content</div>
<div id="tab2">Tab 2 content</div>
<!-- With kelp-tabs: enhanced into tabbed interface -->
<kelp-tabs>
<ul>
<li><a href="#tab1">Tab 1</a></li>
<li><a href="#tab2">Tab 2</a></li>
</ul>
<div id="tab1">Tab 1 content</div>
<div id="tab2">Tab 2 content</div>
</kelp-tabs>
When reviewing existing code
Use the same finding format as the developer skill:
[F1] [Short title] -- [Critical/Major/Minor/Suggestion]
- Location:
file_path:line_number - Issue: [Description]
- Recommended change: [Describe what to change — show wrong vs right HTML]
Specifically flag anti-pattern violations with highest severity.
Anti-Patterns
These are critical violations. Flag them immediately when reviewing code.
1. NEVER use Shadow DOM
<!-- WRONG -->
<my-component>
#shadow-root
<style>...</style>
<slot></slot>
</my-component>
<!-- RIGHT: Light DOM web component -->
<kelp-tabs>
<ul>...</ul>
<div>...</div>
</kelp-tabs>
2. NEVER use JSX or framework components
// WRONG
<Button variant="primary" onClick={handleClick}>Submit</Button>
// WRONG
function Card({ title, children }) { return <div className="card">...</div> }
<!-- RIGHT: Plain HTML -->
<button class="primary">Submit</button>
3. NEVER assume a build step
// WRONG
import styles from './component.module.css'
import { Button } from '@kelp/components'
<!-- RIGHT: Link tags -->
<link rel="stylesheet" href="./css/kelp.css">
4. NEVER use Tailwind-style utility-first
<!-- WRONG: utility-first -->
<h1 class="text-3xl font-bold text-gray-900 mb-4">Title</h1>
<!-- RIGHT: classless HTML (Kelp styles it) -->
<h1>Title</h1>
5. NEVER use className
// WRONG (this is JSX, not HTML)
<button className="primary">Submit</button>
<!-- RIGHT -->
<button class="primary">Submit</button>
6. NEVER write custom JS when a kelp-* web component exists
// WRONG: custom tab implementation
document.querySelectorAll('.tab-button').forEach(btn => {
btn.addEventListener('click', () => { /* show/hide panels */ });
});
<!-- RIGHT: use kelp-tabs -->
<kelp-tabs>
<ul>
<li><a href="#tab1">Tab 1</a></li>
</ul>
<div id="tab1">Content</div>
</kelp-tabs>
7. NEVER use @import for CSS
/* WRONG */
@import url('./kelp.css');
<!-- RIGHT -->
<link rel="stylesheet" href="./kelp.css">
8. NEVER use div-soup when semantic HTML works
<!-- WRONG -->
<div class="nav">
<div class="nav-item" onclick="navigate()">Home</div>
</div>
<!-- RIGHT -->
<nav>
<a href="/">Home</a>
</nav>
9. NEVER add classes to classless elements
<!-- WRONG -->
<h1 class="heading">Title</h1>
<input class="input" type="text">
<p class="paragraph">Text</p>
<!-- RIGHT: Kelp styles these automatically -->
<h1>Title</h1>
<input type="text">
<p>Text</p>
10. NEVER use @apply or CSS-in-JS
/* WRONG */
.my-button { @apply bg-blue-500 text-white px-4 py-2; }
// WRONG
const styles = css`background: blue; color: white;`
/* RIGHT: Use Kelp cascade layers and CSS variables */
@layer kelp.extend {
.my-button {
--background-color: var(--color-blue-50);
--color: white;
}
}
11. NEVER wrap Kelp web components in framework lifecycle
// WRONG: React lifecycle managing Kelp component
useEffect(() => {
const tabs = document.querySelector('kelp-tabs');
tabs.init();
return () => tabs.destroy();
}, []);
<!-- RIGHT: Kelp components self-initialize via progressive enhancement -->
<kelp-tabs>
<ul>...</ul>
<div>...</div>
</kelp-tabs>
<!-- Just include kelp.js — components handle their own lifecycle -->
Iteration
When Sam provides feedback on generated HTML:
- Update only the affected section — do not regenerate the entire page
- Show the complete component after the edit (not fragments)
- Annotate changes with comments:
<!-- UPDATED: [what changed] --> - Flag if a requested change would break progressive enhancement or violate a core principle
Complexity Scaling
Simple tasks (single component, one layout, quick review):
- Show the HTML directly without preamble
- Flag: "Simplified output — request full page structure if needed"
Complex tasks (full page, multiple interacting components):
- Show the layout skeleton first (container, major sections)
- Then detail each section's components
- Include all necessary link tags and scripts
Initiative Integration
When Sam links this to an initiative:
- Read the initiative's PID, PRD, or overview document for context
- Align component selection with the initiative's UI requirements
- Reference the project's AGENTS.md for any Kelp-specific conventions (custom theme, layout patterns)
- The output stays in the conversation for refinement — Sam will decide when to apply changes
Related Skills
developer— suggest loading for architecture decisions, code review beyond Kelp-specific patterns, or backend implementationui-design— suggest loading for design token definitions that map to Kelp CSS variablesux-design— suggest loading for user flow design that informs component selectionseo— suggest loading for semantic HTML optimization and structured data (Kelp's semantic approach directly benefits SEO)qa— suggest loading for test cases covering component states and progressive enhancement behavior
Constraints
- Advisory only: describe changes and show HTML. Do not apply changes without Sam's explicit approval.
- Source of truth is
kelpui.com/docs— not any local repository. - When uncertain about any feature, use WebFetch to check the current docs before recommending.
- Prefer semantic HTML over ARIA workarounds — use native elements first.
- Always verify progressive enhancement: content must be usable without JavaScript.
- If unsure about any aspect, state the uncertainty and ask Sam before proceeding.