← Files pen.devARCHIVED FILE
skills/pen-dev/guide/tailwind.md
10.1 KB · Oct 8, 2026 · 06:38 UTC
# Tailwind v4 Implementation Guidelines
This document provides Tailwind v4 specific guidelines for implementing .pen designs in code.
**NOTE**: These guidelines are specific to Tailwind v4. If you are deliberately using an older version of Tailwind (v3 or earlier), you may bypass the v4-specific syntax rules (such as `@import "tailwindcss";` vs `@tailwind` directives) and adapt accordingly.
## Core Principle
**Use Tailwind classes exclusively throughout - NEVER use inline styles for any property (sizing, colors, spacing, typography, etc.).**
## CSS Variables Setup
### Structure of globals.css
Your `globals.css` should follow this structure:
```css
@import "tailwindcss";
:root {
/* Design variables from .pen file - ONLY single values */
--color-primary: #3b82f6;
--color-secondary: #8b5cf6;
--spacing-base: 16px;
/* DO NOT store font stacks here */
}
@layer base {
html, body {
height: 100%;
}
/* Font family utilities - Define font stacks directly here */
.font-primary {
font-family: "Inter", sans-serif;
}
.font-secondary {
font-family: "JetBrains Mono", monospace;
}
}
```
### Guidelines
- Read design variables using `Print(GetVariables())` in `execute`
- Convert to CSS custom properties in `:root` block for single values only (colors, numbers, keywords)
- Map all design variables using exact names from design file
- **IMPORTANT**: Use `:root` block for design variables (NOT `@theme` - Tailwind v4's `@theme` only supports custom properties and `@keyframes`)
- **DO NOT add manual resets** - `@import "tailwindcss";` includes Preflight automatically
- **CRITICAL for Next.js projects**: If using `next/font` loaders, DO NOT re-wrap their CSS variables (like `--font-geist`) in your `:root` block. Instead, reference them directly in `@layer base` utility classes (see Font Loading section)
## Font Implementation
### Core Rules
**CSS variables work for single values only** (colors, numbers, keywords). **DO NOT use them for font stacks.**
❌ **WRONG**:
```css
:root {
--font-primary: "JetBrains Mono", monospace; /* Breaks with comma-separated values */
}
```
✅ **CORRECT**: Define fonts in `@layer base` utility classes:
```css
@layer base {
.font-primary {
font-family: "JetBrains Mono", monospace;
}
.font-secondary {
font-family: "Inter", sans-serif;
}
}
```
### Next.js Font Loaders
When using `next/font/google` or `next/font/local`:
❌ **NEVER wrap Next.js font variables in `:root`**:
```css
/* WRONG - nested var() references break */
:root {
--font-primary: var(--font-geist);
}
```
✅ **DO reference them directly in utility classes**:
```css
@layer base {
.font-primary {
font-family: var(--font-jetbrains-mono), "JetBrains Mono", monospace;
}
}
```
### Implementation Workflow
1. Read font names from design using `Print(GetVariables())` in `execute`
2. Load fonts via `<link>` tags OR Next.js font loaders in layout.tsx
3. Create utility classes in `@layer base` (`.font-primary`, `.font-secondary`)
4. Use classes in components: `className="font-primary"`
5. **NEVER use** `font-[var(--font-name)]` or inline styles for fonts
## Font Loading
### Tailwind v4 Requirements
❌ **NEVER in Tailwind v4**:
- `@import url()` in CSS files
- `font-[family-name:var(...)]` syntax
- `--turbopack` flag
✅ **Load fonts via**:
- `<link>` tags in layout.tsx `<head>`, OR
- Next.js font loaders (`next/font/google`, `next/font/local`)
### Examples
**Option 1: Manual loading**
```tsx
// layout.tsx
<head>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet" />
</head>
```
**Option 2: Next.js font loaders**
```tsx
// layout.tsx
import { JetBrains_Mono } from "next/font/google";
const jetbrainsMono = JetBrains_Mono({
variable: "--font-jetbrains-mono",
subsets: ["latin"],
});
export default function RootLayout({ children }) {
return (
<html>
<body className={jetbrainsMono.variable}>
{children}
</body>
</html>
);
}
```
```css
/* globals.css */
@layer base {
.font-primary {
font-family: var(--font-jetbrains-mono), "JetBrains Mono", monospace;
}
}
```
## Icon Font Setup
- **If design uses `icon` nodes**:
1. Add Google Fonts link in layout.tsx `<head>` (following the Font Loading Rules above)
2. Add utility class in `@layer base` section of globals.css with appropriate font-feature-settings
3. Render as `<span>` elements with icon name as text content
4. Use inline styles for font-weight if needed (e.g., `style={{ fontWeight: 100 }}`)
5. **NEVER use `@font-face`** - Always use CDN links
## Viewport Setup
- Add `height: 100%` to `html` and `body` in `@layer base` section of globals.css (as shown in CSS Variables Setup example)
- Add `h-full` class to `<html>` and `<body>` in layout.tsx
- Ensures viewport-relative sizing works throughout app
- Design dimensions are specifications, not fixed constraints
- **DO NOT use wildcard selectors** - use the `@layer base` approach shown above
## Tailwind v4 Import and Preflight
### Correct Import Syntax
**Tailwind v4 uses a simplified import syntax** in `globals.css`:
```css
@import "tailwindcss";
```
This single import automatically includes:
- Base styles (Preflight reset)
- Component classes
- Utility classes
**DO NOT use the old v3 syntax**:
```css
/* ❌ WRONG - This is v3 syntax */
@tailwind base;
@tailwind components;
@tailwind utilities;
```
### Preflight Reset Behavior
The `@import "tailwindcss";` automatically includes Preflight, which:
- Removes margins and padding from all elements
- Sets `box-sizing: border-box` on all elements
- Resets headings and lists (they inherit font properties)
- Makes images block-level with responsive sizing
### Critical Rules
- **NEVER manually add global resets** - Preflight handles everything
- **NEVER use wildcard selectors** like `* { margin: 0; padding: 0; }` in globals.css
- **DO NOT duplicate Preflight functionality** - it's already included
- Use `@layer base { ... }` ONLY for additional custom base styles that don't conflict with Preflight
## Layout Conversion
### Container Sizing
- Root containers: `h-full w-full` or `h-screen w-screen` (NOT fixed dimensions)
- Fixed dimensions only for specific elements (e.g., sidebar: `w-[280px]`)
### fill_container Translation
- In flex containers: use `flex-1`
- For explicit sizing: `w-full` (width), `h-full` (height)
- **IMPORTANT**: `h-full` requires parent chain has height set
- For scrollable containers: `flex-1 overflow-auto`
- **NEVER use inline styles** for sizing
- **Multiple fill_container Children**:
* In flex containers, multiple children with `fill_container` → each needs `flex-1`
* Applies to both horizontal and vertical flex layouts
* Distributes space equally among children
- **Height fill_container**:
* ANY component with `height: "fill_container"` MUST have `h-full` class
* Applies universally regardless of component type or parent layout
* Verify every `fill_container` height has corresponding `h-full` class
### fit_content Translation
- Use `w-fit` (width), `h-fit` (height)
- NEVER use inline styles
### Flex Context
- Parent must be flex container for `flex-1` to work
- Use `min-h-0` on flex children that need to shrink below content size
- Scrollable flex children: `flex-1 overflow-auto`
### Verification
- Check ALL `fill_container`/`fit_content` converted to Tailwind classes
- Ensure NO inline styles for width/height
## Style Implementation
Use **Tailwind classes exclusively** (NO inline styles) for all styling:
### 1. Layout
- Position: `relative`, `absolute`, `fixed`, `sticky`
- Display: `flex`, `flex-col`, `grid`, `block`, `inline-block`
- Alignment: `items-center`, `justify-between`, etc.
- Gap: `gap-4`, `gap-[16px]` (match design exactly)
### 2. Spacing
Match design values exactly:
- Padding: `p-4`, `px-6`, `pt-[12px]`, etc.
- Margin: `m-4`, `mx-auto`, `mt-[8px]`, etc.
- Use arbitrary values `[Npx]` when needed
### 3. Dimensions
- Width: `w-[280px]` (fixed), `w-full` or `flex-1` (fill_container), `w-fit` (fit_content)
- Height: `h-[48px]` (fixed), `h-full` or `flex-1` (fill_container), `h-fit` (fit_content)
- Min/max: `min-w-[200px]`, `max-h-[600px]`
- **CRITICAL**: Never use inline styles for dimensions
### 4. Colors and Borders
- Background: `bg-[var(--color-name)]` - NO hardcoded hex values
- Border: `border`, `border-2`, `border-[var(--color-border)]`
- Border radius: `rounded`, `rounded-lg`, `rounded-[12px]`
- Text: `text-[var(--color-text)]`
- Shadows: `shadow-sm`, `shadow-[custom]`
### 5. Typography
- **Font family**: Use utility classes defined in `@layer base` (see "CSS Custom Properties and Font Stacks" section above)
- ✅ Correct: `className="font-primary"`
- ❌ NEVER: `font-[var(--font-primary)]` (arbitrary value syntax doesn't work with CSS variables)
- ❌ NEVER: `style={{ fontFamily: 'var(--font-primary)' }}` (avoid inline styles unless necessary)
- For Next.js font loaders: Create utility classes that reference the Next.js variables, then use those classes
- Font size: `text-sm`, `text-[14px]`
- Font weight: `font-medium`, `font-[500]`
- Line height: `leading-normal`, `leading-[24px]`
- Letter spacing: `tracking-normal`, `tracking-[0.02em]`
### 6. Interactive States
- Hover: `hover:bg-[var(--color-hover)]`, `hover:opacity-80`
- Active: `active:scale-95`
- Disabled: `disabled:opacity-50`, `disabled:cursor-not-allowed`
- Focus: `focus:outline-none`, `focus:ring-2`
## SVG Styling
For SVG path extraction and implementation workflow, see code.md "SVG Path Implementation" section.
### Tailwind-Specific SVG Styling
When styling SVG elements with Tailwind:
- **Fill colors**: Use `fill-[var(--color-name)]` with CSS variables
- Example: `fill-[var(--primary)]`
- **Stroke colors**: Use `stroke-[var(--color-name)]`
- Example: `stroke-[var(--border)]`
- **Stroke width**: Use `stroke-[2]` or arbitrary values `stroke-[1.5px]`
- **SVG sizing**: Use standard sizing classes `w-6 h-6` or arbitrary `w-[24px] h-[24px]`
- **NEVER use inline styles** - always use Tailwind classes or className with CSS variables
Example:
```tsx
<svg className="w-6 h-6 fill-[var(--icon-primary)]" viewBox="0 0 24 24">
<path d="M12 2L2 7l10 5 10-5-10-5z" />
</svg>
```
SHA-256: b426d429e27ab68ce32767146b67789cb465fbb079811d90889156a84e4422e5