emily.config.json → emily.css
Define your brand colours, fonts, and spacing once. Emily generates every utility you need and gives you production-ready components to copy straight into any project.
Add your brand colours to emily.config.json. Fonts, spacing, and breakpoints too. One file, everything in one place.
{
"colours": {
"primary": "#DB2777",
"secondary": "#2563EB"
}
}
Run the build script. Emily generates a 10-shade scale per colour, plus every utility you need. Purge unused classes to get down to 10–50 KB.
node src/index.js --purge . # Before: 1.1 MB # After: ~39 KB
Link the stylesheet. Browse the components below. Copy the HTML, paste into your project. Works in Drupal, static HTML, Power Pages — anything that loads a CSS file.
<link rel="stylesheet" href="dist/emily.purged.css">
From zero to a working design system in under 30 minutes.
git clone https://github.com/yourusername/emilyui.git cd emilyui
emily.config.jsonSet your brand colours. Shade 80 is always your input hex — the rest of the scale is generated automatically.
{
"colours": {
"primary": "#DB2777",
"secondary": "#2563EB",
"success": "#059669",
"warning": "#D97706",
"error": "#DC2626",
"neutral": "#57534E"
},
"fontFamily": "inter"
}
Point --purge at any directory. Emily scans your HTML, Twig, Vue, JSX — whatever you use.
node src/index.js --purge .
Drop this into your <head>. No JavaScript, no build pipeline, no dependencies.
<link rel="stylesheet" href="dist/emily.purged.css">
Classes follow a consistent naming pattern. Responsive with md:, lg: prefixes. State variants with hover:, focus:.
<!-- Colour --> bg-primary-80 text-primary-80 border-primary-80 <!-- Spacing --> p-4 px-6 py-3 m-2 mt-8 gap-4 <!-- Typography --> text-xl font-semibold text-neutral-60 <!-- Responsive --> text-base md:text-lg lg:text-xl <!-- State --> hover:bg-primary-90 focus-visible:outline-2
The most common utility categories. Every class follows the same pattern — change the value, the result changes.
bg-primary-80
text-primary-80
border-primary-80
bg-primary-10
bg-{colour}-{10–100}
p-4 px-6 py-3
mt-8 mb-4 mx-auto
gap-4 gap-6
p-{0–96} m-{0–96}
text-xl text-sm
font-semibold font-bold
text-neutral-60
uppercase tracking-wide
text-{xs–4xl}
flex flex-col flex-wrap
items-center items-start
justify-between
shrink-0 flex-1
grid grid-cols-2
grid-cols-3 grid-cols-4
gap-4 gap-6
grid-cols-{1–12}
border border-2 border-4
border-neutral-30
rounded rounded-md
rounded-lg rounded-full
shadow-sm
shadow
shadow-md
shadow-lg
hover:bg-primary-90
focus-visible:outline-2
cursor-not-allowed
transition sr-only
md: lg: hover: focus:
Each config colour generates a 10-shade scale. Shade 80 is your input hex. Use bg-{colour}-{shade}, text-{colour}-{shade}, border-{colour}-{shade}.
Type scale and font weight utilities. Use text-{size} and font-{weight}.
Padding, margin, and gap utilities. Use p-{n}, m-{n}, gap-{n}.
Use shadow-{size} and rounded-{size}.
Production-ready. Copy the HTML, paste into your project. All accessibility attributes included.
<!-- Primary -->
<button class="bg-primary-80 text-white px-5 py-2 rounded font-medium text-sm hover:bg-primary-90 focus-visible:outline-2 focus-visible:outline-primary-80 transition cursor-pointer border-0">
Primary
</button>
<!-- Outline -->
<button class="bg-white text-primary-80 px-5 py-2 rounded font-medium text-sm border-2 border-primary-80 hover:bg-primary-10 focus-visible:outline-2 focus-visible:outline-primary-80 transition cursor-pointer">
Outline
</button>
<!-- Disabled -->
<button class="bg-neutral-20 text-neutral-50 px-5 py-2 rounded font-medium text-sm cursor-not-allowed border-0" disabled aria-disabled="true">
Disabled
</button>
Includes focus-visible, error state with aria-invalid, and disabled variant.
<div class="flex flex-col gap-1">
<label for="email" class="text-sm font-medium text-neutral-80">Email address</label>
<input id="email" type="email" placeholder="you@example.com"
class="w-full px-3 py-2 border-2 border-neutral-30 rounded text-sm text-neutral-90 bg-white focus-visible:outline-2 focus-visible:outline-primary-80 transition" />
</div>
<!-- Error state -->
<div class="flex flex-col gap-1">
<label for="email-error" class="text-sm font-medium text-neutral-80">Email address</label>
<input id="email-error" type="email" aria-invalid="true" aria-describedby="email-error-msg"
class="w-full px-3 py-2 border-2 border-error-80 rounded text-sm text-neutral-90 bg-white focus-visible:outline-2 focus-visible:outline-error-80 transition" />
<span id="email-error-msg" class="text-xs text-error-80" role="alert">Enter a valid email address.</span>
</div>
A simple card with a title, some body text, and an action.
Highlighted card for key features or calls to action.
<div class="bg-white border-2 border-neutral-20 rounded-lg p-5 shadow-sm flex flex-col gap-3">
<h2 class="text-base font-semibold text-neutral-90">Card title</h2>
<p class="text-sm text-neutral-60">Card body text goes here.</p>
<button class="bg-primary-80 text-white px-4 py-2 rounded text-sm font-medium hover:bg-primary-90 transition cursor-pointer border-0 self-start">
View details
</button>
</div>
Changes saved
Your settings have been updated successfully.
Something went wrong
We couldn't save your changes. Please try again.
Review required
This action will affect 23 connected records.
Heads up
Scheduled maintenance on Sunday 29 April, 2–4am.
<!-- Success alert -->
<div class="bg-success-10 border-2 border-success-30 rounded p-4 flex gap-3 items-start" role="alert">
<!-- icon -->
<div>
<p class="text-sm font-semibold text-success-90">Changes saved</p>
<p class="text-sm text-success-80">Your settings have been updated successfully.</p>
</div>
</div>
<!-- Error alert -->
<div class="bg-error-10 border-2 border-error-30 rounded p-4 flex gap-3 items-start" role="alert">
<!-- icon -->
<div>
<p class="text-sm font-semibold text-error-90">Something went wrong</p>
<p class="text-sm text-error-80">We couldn't save your changes. Please try again.</p>
</div>
</div>