🍅 Tomato CSS
The human-friendly CSS preprocessor. Write styles like you think.
Installation
Global Install (Recommended)
$ npm install -g @srivtx/tomato-css
$ tomato app.tom -o styles.css
Project Install
$ npm install @srivtx/tomato-css
$ npx tomato app.tom
Or add to package.json:
{
"scripts": {
"build": "tomato src/app.tom -o dist/styles.css",
"watch": "tomato --watch src/app.tom -o dist/styles.css"
}
}
No Install (npx)
$ npx @srivtx/tomato-css app.tom
Your First .tom File
Create a file called styles.tom:
button:
bg blue-500
color white
pad 2 4
round lg
pointer
smooth
Compile it:
$ tomato styles.tom -o styles.css
CLI Usage
$ tomato <input.tom> [-o output.css]
$ tomato --watch <input.tom> [-o output.css]
tomato app.tom |
Compile to app.css |
tomato app.tom -o styles.css |
Custom output file |
tomato --watch app.tom |
Watch mode |
Selectors
HTML Elements
button:
bg blue-500
nav:
row spread
input:
pad 2 4
Classes
.btn-primary:
bg blue-500
color white
.card:
bg white
round xl
shadow lg
.hero-title:
size 4xl
bold
IDs
#header:
bg slate-900
pad 4 6
#main-content:
pad 8
Auto-Class Conversion
Names that aren't HTML elements become classes automatically:
// These are equivalent:
card: // → .card { }
.card: // → .card { }
// But these stay as elements:
button: // → button { }
nav: // → nav { }
HTML elements like button, nav, div, header, footer, etc. stay as element selectors.
Everything else becomes a class.
Components
Define reusable style groups with component name: and use them with
use name.
Basic Components
component btn:
pad 2 4
round lg
bold
pointer
smooth
no border
button:
use btn
bg blue-500
color white
.btn-secondary:
use btn
bg violet-500
color white
.btn-outline:
use btn
bg transparent
color blue-500
border 2px solid blue-500
Nested Components
component base-input:
pad 3 4
round md
border 1px solid slate-300
smooth
component focus-ring:
use base-input
focus:
ring 2px solid blue-400
border-color blue-500
input:
use focus-ring
w-full
When you
use a component, all its properties are expanded inline. This
means you can override them by adding your own after!Card Components
component card:
bg white
round xl
shadow md
pad 6
smooth
hover:
shadow lg
transform translateY(-2px)
.product-card:
use card
column
gap 4
.profile-card:
use card
center all
pad 8
Form Input Components
component input-base:
w-full
pad 3 4
round lg
border 1px solid slate-200
bg white
smooth
focus:
border-color blue-500
ring 2px solid blue-100
no outline
input:
use input-base
textarea:
use input-base
min-height 120px
resize vertical
select:
use input-base
pointer
Layout Components
component container:
max-width 1200px
margin 0 auto
pad 4 6
component stack:
column
gap 4
component row-layout:
row
gap 4
align-items center
.page:
use container
use stack
gap 8
.toolbar:
use row-layout
row spread
pad 4
bg slate-50
You can use multiple components with multiple
use statements!
Properties are applied in order, so later ones override earlier ones.Pseudo States
button:
bg blue-500
color white
smooth
hover:
bg blue-600
shadow lg
focus:
ring 2px solid blue-400
no outline
active:
bg blue-700
disabled:
opacity 0.5
cursor not-allowed
Responsive Design
hero:
pad 16
grid 3
gap 6
@mobile:
pad 4
grid 1
@tablet:
grid 2
| Query | Breakpoint |
|---|---|
@mobile: |
max-width: 640px |
@tablet: |
max-width: 768px |
@laptop: |
max-width: 1024px |
@desktop: |
max-width: 1280px |
Custom Tokens
colors:
primary blue-500
secondary violet-500
dark slate-900
light slate-50
muted slate-400
danger red-500
success green-500
button:
bg primary
color light
.alert-danger:
bg danger
color white
Color Reference
// 22 color scales built-in (Tailwind):
slate, gray, zinc, neutral, stone
red, orange, amber, yellow, lime
green, emerald, teal, cyan, sky
blue, indigo, violet, purple
fuchsia, pink, rose
// Usage:
bg rose-500
color slate-900
border 1px solid blue-300
All Properties
| Tomato | CSS Output |
|---|---|
bg blue-500 |
background: #3b82f6 |
color white |
color: #ffffff |
pad 2 4 |
padding: 0.5rem 1rem |
margin 4 |
margin: 1rem |
round lg |
border-radius: 0.5rem |
round full |
border-radius: 9999px |
shadow md |
box-shadow: ... |
bold |
font-weight: bold |
semibold |
font-weight: 600 |
italic |
font-style: italic |
uppercase |
text-transform: uppercase |
underline |
text-decoration: underline |
pointer |
cursor: pointer |
smooth |
transition: all 0.2s ease |
no border |
border: none |
no outline |
outline: none |
row |
display: flex; flex-direction: row |
row spread |
...justify-content: space-between |
row center |
...justify-content: center |
column |
display: flex; flex-direction: column |
center all |
...justify-content: center; align-items: center |
grid 3 |
display: grid; grid-template-columns: repeat(3, 1fr) |
gap 4 |
gap: 1rem |
w-full |
width: 100% |
h-screen |
height: 100vh |
relative |
position: relative |
absolute |
position: absolute |
fixed |
position: fixed |
hidden |
display: none |
overflow hidden |
overflow: hidden |