Styling logo

Styling

OrganizationPopular
EpicenterHQ
styling

CSS and Tailwind, cn(), flex layouts. Use for "style this", "fix the CSS", "add classes", "not scrolling", "overflow", Tailwind utilities.

Overview

PublisherEpicenterHQ
Repositoryepicenter
Skill namestyling
Stars
4.8K
Forks
386
Bundled files
Instructions only
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by EpicenterHQ on GitHub. Read the source before you install it.

Installation

Install the Styling AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/EpicenterHQ/epicenter/tree/main/.agents/skills/styling
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/EpicenterHQ/epicenter.git /tmp/epicenter
mkdir -p .claude/skills
cp -r /tmp/epicenter/.agents/skills/styling .claude/skills/styling
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Styling in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Styling on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Styling is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Styling Guidelines

When styling depends on shadcn-svelte structure, class merging, variants, Bits UI composition, or local wrapper behavior, read ui-design's component-system reference for upstream grounding. Ordinary Tailwind utilities and the repo-local layout rules below need no external lookup.

Minimize Wrapper Elements

Avoid creating unnecessary wrapper divs. If classes can be applied directly to an existing semantic element with the same outcome, prefer that approach.

Good (Direct Application)

svelte
<main class="flex-1 mx-auto max-w-7xl">
	{@render children()}
</main>

Avoid (Unnecessary Wrapper)

svelte
<main class="flex-1">
	<div class="mx-auto max-w-7xl">
		{@render children()}
	</div>
</main>

This principle applies to all elements where the styling doesn't conflict with the element's semantic purpose or create layout issues.

Tailwind Best Practices

  • Use cn() from @epicenter/ui/utils in apps and ../utils.js inside the UI package for combining classes conditionally
  • Prefer utility classes over custom CSS for local layout and state
  • Prefer shared scale and semantic-token utilities over arbitrary bracketed values and raw colors
  • Use tailwind-variants for component variant systems
  • Follow the background/foreground convention for colors
  • Leverage CSS variables for theme consistency

Shared Primitive Overrides

When styling a local @epicenter/ui primitive, use ui-design's component-system reference. Tailwind classes on shared primitives should usually express parent layout or product state, not redefine the primitive's visual budget.

Good primitive overrides:

svelte
<Item.Button size="sm" class="w-full justify-start text-left" />

Suspicious primitive overrides:

svelte
<Item.Button size="sm" class="gap-2 rounded px-2 py-1.5 text-sm hover:bg-accent/50" />

If an override repeats size, density, radius, gap, padding, typography, hover, focus, or transition classes, ask whether the primitive needs a size, variant, or semantic wrapper instead.

Disabled States: Use HTML disabled + Tailwind Variants

When an interactive element can be non-interactive (empty section, loading state, no items), use the HTML disabled attribute instead of JS conditional guards. Pair it with Tailwind's enabled: and group-disabled: variants.

Why disabled Over JS Guards

  • disabled natively blocks clicks: no if (!hasItems) return needed
  • Enables the :disabled CSS pseudo-class for styling
  • Semantically correct for accessibility (screen readers announce "dimmed" or "unavailable")
  • Tailwind's enabled: and group-disabled: variants compose cleanly

Pattern

svelte
<!-- The button disables itself when count is 0 -->
<button
  class="group enabled:cursor-pointer enabled:hover:opacity-80"
  disabled={item.count === 0}
  onclick={toggle}
>
  {item.label} ({item.count})
  <ChevronIcon class="group-disabled:invisible" />
</button>

Key Variants

  • enabled:cursor-pointer: pointer cursor only when clickable
  • enabled:hover:bg-accent/50: hover effects only when interactive
  • group-disabled:invisible: hide child elements (e.g., expand chevron) when parent is disabled
  • disabled:opacity-50: dim the element when disabled

Anti-Pattern

svelte
<!-- Don't do this: JS guard duplicates what disabled does natively -->
<button
  class="cursor-pointer hover:opacity-80"
  onclick={() => { if (item.count > 0) toggle(); }}
>

The JS guard leaves cursor-pointer and hover:opacity-80 active on a non-interactive element. The user sees a clickable button that does nothing. Use disabled and let the browser + CSS handle it.

Flex Column Scroll Trap

When a flex child uses h-full (height: 100%) but shares a flex column with siblings (headers, toolbars, footers), it computes to the full parent height: overflowing past siblings instead of taking the remaining space. The content gets clipped or pushes the layout past the viewport, and scroll areas inside never activate.

This is the single most common layout bug in this codebase. It appears whenever you have:

  • A component inside a Resizable.Pane (paneforge) that needs to scroll
  • A ScrollArea.Root (bits-ui) or overflow-auto div inside a flex column with a header/toolbar sibling
  • Any split-pane or panel layout where one section should scroll independently

The Fix: flex-1 min-h-0 overflow-hidden

Replace h-full with these three utilities on the flex child that contains scrollable content. Each solves a distinct problem:

UtilityWhat it doesWhy it's needed
flex-1Take remaining space after siblingsh-full = 100% of parent, ignoring siblings. flex-1 = remaining space.
min-h-0Allow shrinking below content sizeFlex items default to min-height: auto, preventing them from being smaller than their content.
overflow-hiddenEstablish a bounded height contextWithout this, children with overflow-auto or ScrollArea have no height ceiling to scroll against.

All three are required. Missing any one breaks the fix:

  • Without flex-1: element is still 100% of parent, overflows siblings
  • Without min-h-0: element refuses to shrink, content pushes it taller
  • Without overflow-hidden: inner scroll containers have no bounded ancestor, so they expand instead of scrolling

Before / After

svelte
<!-- BROKEN: h-full = 100% of parent, ignores the toolbar sibling -->
<main class="flex h-full flex-col overflow-hidden">
  <div class="border-b px-4 py-2">Toolbar</div>
  <MyScrollableContent class="h-full" />  <!-- overflows past main -->
</main>

<!-- FIXED: flex-1 takes remaining space, overflow-hidden bounds it -->
<main class="flex h-full flex-col overflow-hidden">
  <div class="border-b px-4 py-2">Toolbar</div>
  <MyScrollableContent class="flex-1 min-h-0 overflow-hidden" />
</main>

Inside Resizable Panes (paneforge)

Paneforge Pane components set width via flex ratios but do not constrain height or clip overflow. Any scrollable content inside a Pane needs the full flex-1 min-h-0 overflow-hidden chain on its root element:

svelte
<Resizable.Pane defaultSize={80}>
  <!-- Pane provides no height constraint or overflow clipping -->
  <div class="flex flex-1 min-h-0 flex-col overflow-hidden">
    <div class="border-b">Header</div>
    <div class="flex-1 overflow-y-auto">
      <!-- this content now scrolls -->
    </div>
  </div>
</Resizable.Pane>

With ScrollArea (bits-ui)

ScrollArea.Root renders with position: relative and its viewport uses height: 100%. This breaks the flex sizing chain: the viewport's percentage height resolves against the relative parent, which has no explicit height in a flex context. The content expands instead of scrolling.

Two options:

  1. Prefer plain overflow-y-auto on a div with flex-1 min-h-0 (simpler, always works)
  2. If you need styled scrollbars, wrap ScrollArea.Root in a div with flex-1 min-h-0 overflow-hidden to give it a bounded ancestor
svelte
<!-- Option 1: Plain overflow (preferred) -->
<div class="flex-1 overflow-y-auto">
  {#each items as item}
    <div>{item.name}</div>
  {/each}
</div>

<!-- Option 2: ScrollArea with bounded wrapper -->
<div class="flex-1 min-h-0 overflow-hidden">
  <ScrollArea.Root class="h-full">
    {#each items as item}
      <div>{item.name}</div>
    {/each}
  </ScrollArea.Root>
</div>

Rule of Thumb

If you write h-full on a flex child that has siblings in the same flex column, stop and replace it with flex-1 min-h-0 overflow-hidden. The h-full pattern only works when the element is the sole child of its flex parent.

Frequently asked questions

What does the Styling AI skill do?

CSS and Tailwind, cn(), flex layouts. Use for "style this", "fix the CSS", "add classes", "not scrolling", "overflow", Tailwind utilities.

Why use Styling on TypingMind?

Because you install it once and use it with any model. Styling is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Styling in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/EpicenterHQ/epicenter/tree/main/.agents/skills/styling. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Styling?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Styling?

As many as you like. As long as a model supports skills, you can use Styling with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Styling AI skill free?

It is published on GitHub by EpicenterHQ. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇