Tailwind CSS
LogicStamp Context extracts Tailwind CSS utility classes, responsive breakpoints, and design patterns from your components.
Tailwind Detection
LogicStamp detects Tailwind CSS by parsing your TypeScript/JavaScript with ts-morph to extract classes from JSX attributes, detecting utility class patterns, and reading your Tailwind config when available. It also identifies Tailwind's class naming conventions and detects breakpoint prefixes (sm:, md:, lg:, etc.).
What Gets Extracted
Utility Classes
All Tailwind utility classes are extracted and categorized. Variant prefixes (responsive, pseudo-classes, etc.) are stripped before categorization, so classes are grouped by their base utility:
function Card() {
return (
<div className="flex flex-col gap-4 p-6 bg-white rounded-lg shadow-md">
<h2 className="text-2xl font-bold text-gray-900">Title</h2>
<p className="text-gray-600">Description</p>
</div>
);
}Extracted categories:
Layout
flex,grid,block,inline,hidden,containerbox-*,aspect-*,columns-*,break-*
Spacing
- Padding:
p-*,px-*,py-*,pt-*,pb-*,pl-*,pr-* - Margin:
m-*,mx-*,my-*,mt-*,mb-*,ml-*,mr-* - Gap:
gap-*,space-*
Sizing
- Width:
w-*,w-full,w-screen,w-auto - Height:
h-*,h-full,h-screen,h-auto - Min/Max:
min-w-*,max-w-*,min-h-*,max-h-* - Size:
size-*
Typography
- Font size:
text-xs,text-sm,text-base,text-lg,text-xl,text-2xl, etc. - Font weight:
font-* - Line height:
leading-* - Letter spacing:
tracking-* - Text transforms:
uppercase,lowercase,capitalize,normal-case - Text utilities:
antialiased,subpixel,italic,not-italic,truncate,whitespace-*
Colors
- Background:
bg-*(e.g.,bg-blue-500,bg-white,bg-gray-100) - Text:
text-*(e.g.,text-gray-900,text-blue-600) - Border:
border-*(e.g.,border-gray-300) - Outline:
outline-* - Gradients:
from-*,via-*,to-* - Text decoration:
decoration-*
Note: shadow-* utilities are categorized under Effects, not Colors. ring-* utilities are categorized under Borders, not Colors.
Borders
- Border styles:
border,border-* - Border radius:
rounded,rounded-* - Ring:
ring-*,ring-offset-* - Dividers:
divide-*
Effects
- Shadows:
shadow,shadow-md,shadow-lg,shadow-xl(categorized here, not in colors) - Opacity:
opacity-* - Mix blend:
mix-* - Backdrop:
backdrop-* - Filters:
blur-*,brightness-*,contrast-*,grayscale,hue-*,invert,saturate,sepia
Transitions
- Transitions:
transition-*,duration-*,ease-*,delay-* - Animations:
animate-*
Transforms
- Transform utilities:
scale-*,rotate-*,translate-*,skew-*,origin-*
Interactivity
- Cursor:
cursor-* - Selection:
select-* - Pointer events:
pointer-events-* - Resize:
resize-* - Scroll:
scroll-* - Touch:
touch-* - Will change:
will-*
SVG
- Fill:
fill-* - Stroke:
stroke-*
Other
- Any utility classes that don't match the above categories are placed in the
othercategory
Variant Prefixes
The extractor handles complex variant chains, stripping all variant prefixes before categorization:
<div className="
group-hover:dark:sm:bg-red-500
aria-[checked=true]:peer-focus:ring-2
md:hover:bg-blue-500
light:bg-white
">
Content
</div>Supported variant types:
Responsive Breakpoints
sm:,md:,lg:,xl:,2xl:- Standard breakpointsmax-sm:,max-md:,max-lg:,max-xl:,max-2xl:- Max-width breakpoints
Breakpoints are extracted from anywhere in the class name, not just at the start (e.g., hover:sm:bg-red-500).
Pseudo-classes
hover:,focus:,active:,disabled:,visited:first:,last:,odd:,even:before:,after:,placeholder:,file:,marker:,selection:first-line:,first-letter:,backdrop:
State Variants
open:,checked:,selected:,indeterminate:required:,optional:,valid:,invalid:in-range:,out-of-range:,read-only:,read-write:empty:,target:
Theme Variants
dark:- Dark modelight:- Light mode
Motion Variants
motion-safe:- Only apply when motion is safemotion-reduce:- Only apply when motion is reduced
ARIA/Data Variants
aria-[...]:- ARIA attribute variants (e.g.,aria-[pressed=true]:)data-[...]:- Data attribute variants (e.g.,data-[state=active]:)
Group/Peer Variants
group-hover:,group-focus:,group-active:peer-hover:,peer-focus:,peer-active:
print:- Print media queries
Examples:
sm:bg-red-500→ categorized ascolors(base:bg-red-500)md:hover:bg-red-500→ categorized ascolors(base:bg-red-500)dark:focus:ring-2→ categorized asborders(base:ring-2)group-hover:dark:sm:bg-red-500→ categorized ascolors(base:bg-red-500)aria-[checked=true]:peer-focus:ring-2→ categorized asborders(base:ring-2)
Class Extraction
The extractor uses AST-based analysis (via ts-morph) to extract classes from various patterns:
Literal Strings
// ✅ Extracted - literal strings
<div className="px-4 py-2 rounded bg-blue-500">
Content
</div>Template Literals
// ✅ Extracted - static segments from template literals
// Dynamic expressions within ${} are ignored
<div className={`px-4 py-2 ${isActive ? 'bg-blue-500' : 'bg-gray-500'}`}>
Content
</div>
// ✅ Extracted - backticks with no interpolations
<div className={`flex p-4`}>
Content
</div>Conditional Expressions
// ✅ Extracted - conditional expressions at top level
<div className={isActive && 'bg-blue-500'}>
Content
</div>Function Calls (cn/clsx/classnames)
// ✅ Extracted - cn() with string arguments
<div className={cn('flex', 'p-4', 'bg-white')}>
Content
</div>
// ✅ Extracted - cn() with conditional arguments
<div className={cn('flex', isActive && 'bg-blue-500', 'text-white')}>
Content
</div>
// ✅ Extracted - cn() with template literal arguments
<div className={cn('flex', `p-${size}`, isActive && 'bg-blue-500')}>
Content
</div>Both className and class Attributes
// ✅ Extracted - React/Preact (className)
<div className="flex p-4">
Content
</div>
// ✅ Extracted - Vue/Svelte/etc. (class)
<div class="flex p-4">
Content
</div>Note: The extractor automatically deduplicates classes, so if the same class appears multiple times, it will only be included once in the output.
Custom Tailwind Classes
Custom classes from your Tailwind configuration are detected:
// Using custom classes from tailwind.config.js
<div className="custom-card custom-shadow">
Content
</div>Style Extraction
Style output shape: Contracts expose style data under style (see UIF contracts). stamp context style and stamp context --include-style default to --style-mode lean: Tailwind output keeps classCount, categoriesUsed, and optional breakpoints, and omits per-category class arrays. Use --style-mode full for style.styleSources.tailwind.categories (record of category name → class arrays). Both modes use the same extraction. See Style mode: lean vs full.
When style extraction is enabled, Tailwind classes are included under style.styleSources.tailwind.
Example (--style-mode full):
{
"style": {
"styleSources": {
"tailwind": {
"categories": {
"layout": ["flex", "flex-col"],
"spacing": ["gap-4", "p-6"],
"sizing": ["w-full"],
"typography": ["font-bold", "text-2xl", "text-gray-900"],
"colors": ["bg-blue-500", "bg-white", "text-gray-600"],
"borders": ["ring-2", "rounded-lg"],
"effects": ["opacity-50", "shadow-md"],
"transitions": ["transition-colors"],
"transforms": ["scale-105"],
"interactivity": ["cursor-pointer"],
"svg": ["fill-blue-500"],
"other": ["custom-class"]
},
"breakpoints": ["sm", "md", "lg"],
"classCount": 42
}
},
"summary": {
"mode": "full",
"sources": ["tailwind"]
}
}
}Example (--style-mode lean, default):
{
"style": {
"styleSources": {
"tailwind": {
"classCount": 42,
"categoriesUsed": ["layout", "spacing", "colors", "typography", "borders", "effects"],
"breakpoints": ["sm", "md", "lg"]
}
},
"summary": {
"mode": "lean",
"sources": ["tailwind"],
"fullModeBytes": 2100
}
}
}Output format:
- Full mode:
categoriesis a record of category → sorted class arrays (up to 15 classes per category). Classes keep full variant prefixes (e.g.,md:hover:bg-blue-500). Breakpoints are listed separately. Uncategorized utilities go underother. - Lean mode: No
categoriesobject;categoriesUsedlists which category keys had classes.classCountis the total number of distinct classes collected.summary.fullModeBytes(when present) estimates serialized size of the full-mode tailwind object for comparison. - Note: Unlike component libraries that cap total components, Tailwind caps per category in full mode (up to 15 layout, 15 spacing, etc.).
Usage
# Extract Tailwind classes (lean style output by default)
stamp context --include-style
# Or use the style command
stamp context style
# Verbose per-category class arrays
stamp context style --style-mode fullTailwind Configuration
Note: LogicStamp does not read tailwind.config.js files. It extracts Tailwind classes directly from your source code using AST-based analysis. Custom classes defined in your Tailwind configuration will be detected if they appear in your component code, but the configuration file itself is not parsed.
The extractor works by:
- Analyzing className attributes in JSX
- Extracting utility classes from your source code
- Categorizing classes by type (layout, spacing, colors, etc.)
- Detecting breakpoint prefixes from class names
Custom classes from your Tailwind config (e.g., custom-card, custom-shadow) will be extracted if they're used in your components, but they'll be categorized as "other" since they don't match standard Tailwind utility patterns.
Best Practices
- Use consistent spacing: Stick to Tailwind's spacing scale
- Organize classes: Group related utilities together
- Use responsive prefixes: Leverage breakpoint prefixes for responsive design
- Extract components: Use component extraction for repeated patterns
- Custom utilities: Define custom utilities in config for reuse
Common Patterns
Card Component
function Card({ children }: { children: React.ReactNode }) {
return (
<div className="bg-white rounded-lg shadow-md p-6">
{children}
</div>
);
}Responsive Grid
function Grid({ items }: { items: Item[] }) {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{items.map(item => <ItemCard key={item.id} item={item} />)}
</div>
);
}Button Variants
function Button({ variant = 'primary' }: { variant?: 'primary' | 'secondary' }) {
// ✅ Extracted - cn() with conditionals
return (
<button className={cn(
'px-4 py-2 rounded font-medium transition-colors',
variant === 'primary'
? 'bg-blue-500 text-white hover:bg-blue-600'
: 'bg-gray-200 text-gray-900 hover:bg-gray-300'
)}>
Click me
</button>
);
}
// ✅ Also works with template literals
function ButtonAlt({ variant = 'primary' }: { variant?: 'primary' | 'secondary' }) {
const baseClasses = 'px-4 py-2 rounded font-medium transition-colors';
const variantClasses = variant === 'primary'
? 'bg-blue-500 text-white hover:bg-blue-600'
: 'bg-gray-200 text-gray-900 hover:bg-gray-300';
return (
<button className={`${baseClasses} ${variantClasses}`}>
Click me
</button>
);
}Complex Variant Chains
function InteractiveCard() {
return (
<div className="
group-hover:dark:sm:bg-red-500
aria-[checked=true]:peer-focus:ring-2
md:hover:bg-blue-500
light:bg-white
motion-safe:transition-all
">
Content
</div>
);
}Error Handling
The Tailwind extractor is designed to be robust and will never crash the CLI, even when encountering:
- Malformed JSX: Invalid or unclosed tags are handled gracefully
- Parse errors: AST parsing failures fall back to regex-based extraction
- Invalid inputs: Non-array inputs to categorization/breakpoint functions return empty results
- Unexpected expressions: Dynamic or complex expressions that can't be statically analyzed are silently skipped
All error handling is silent by default. To enable debug logging for troubleshooting, set the LOGICSTAMP_DEBUG=1 environment variable:
LOGICSTAMP_DEBUG=1 logicstamp-context --include-styleThis will output error messages to help identify problematic files or expressions.
Limitations
Current Limitations
-
Array/Object literals in cn(): Array syntax like
cn(['foo', 'bar'])and object syntax likecn({ 'foo': true, 'bar': isActive })are not currently supported. Only direct arguments are extracted. -
Arbitrary values: Arbitrary values (e.g.,
w-[123px],bg-[#ff0000]) are detected but not fully parsed. -
JIT mode: JIT mode classes are extracted but may not be validated against your Tailwind configuration.
-
Custom plugins: Custom Tailwind plugins may not be fully recognized in categorization.
-
Runtime classes: Dynamic class generation at runtime (e.g., classes stored in variables and concatenated later) is not tracked.
-
Template literal expressions: Static segments and Phase 1 dynamic expressions (variables, object properties, conditionals) are extracted (v0.3.9+). Phase 2 patterns (object lookups with variables, cross-file references, function calls) are not yet resolved.
What's Supported
✅ AST-based extraction using ts-morph for accurate parsing
✅ Literal strings: className="flex p-4"
✅ Template literals: Static segments from className={`flex ${var}`}
✅ Conditional expressions: className={isActive && 'bg-blue'}
✅ Function calls: cn(), clsx(), classnames() with direct arguments
✅ Both attributes: className (React/Preact) and class (Vue/Svelte/etc.)
✅ Early deduplication: Classes are deduplicated as they're collected
Related Documentation
- Style Extraction - Complete style extraction guide
- ShadCN Support - ShadCN/UI (built on Tailwind)
- React Support - React component patterns
Next Steps
Explore other UI frameworks or learn about style metadata extraction.