properties
changelog

Styling With Intent

With Shilp CSS, you don’t build styles by accumulating utilities in HTML, You compose them in CSS by clearly stating what an element is responsible for.


Thinking in Responsibilities

When styling an element, think in terms of intent:

  • How it look like?
  • How does space behave?
  • How should text appear?
  • What colors does it use?
  • How does it react to state or context?

Each of these answers belongs to a separate intent.

Intent

Shilp CSS groups styling concerns explicitly by intent.

Each intent exists to hold one kind of a decision.

IntentPurposeUtilities
@bgBackground stylescolor, gradient, image, etc
@textTypography stylessize, weight, color, alignment, etc
@layoutLayout stylesdisplay, layer, overflow, shadow, etc
@flexFlex stylesalign, justify, grow, shrink, etc
@spaceSpacing stylespadding, margin and gap
@borderBorder stylescolor, radius, thickness, etc
@animateAnimation stylesenter, exit, delay, duration, etc

and it goes on...

Example

Using intent to build component
.card {
	@bg color-surface;
	@text color-surface-fg;
	@layout is-flex shadow-sm shadow-color-fg/5;
	@flex flow-col;
	@border thick rounded-xl;
	@space py-6 gap-6;
}

Each intent exists to hold a single responsibility.

Stateful Intents (Mixins)

Stateful intents applies styles only when certain conditions are met.

These are implemented as mixins and can:

  • coexist with regular and other stateful intents
  • nest within each other
  • changes scope clearly
DirectivePurposeVariants
@stateUI stateshover, focus, disabled
@themeTheme variationsdark, light
@childsChild selectorsfirst, last, any, direct
@screenResponsive breakpointssm, md, xl

and it goes on...

Example

Using stateful intent (mixins) to build component
.card {
	/* Regular intents */
	@bg color-surface;
	@text color-surface-fg;
	@layout is-flex shadow-sm shadow-color-fg/5;
	@flex flow-col;
	@border thick rounded-xl;
	@space py-6 gap-6;

	/* Stateful intents ( responsive + state ) */
	@screen xl {
		@space py-8 gap-10;

		@state hover {
			@border thick-2;
		}
	}

	/* Stateful intents ( theme ) */
	@theme dark {
		@bg color-surface/90;
		@text color-surface-fg/90;
	}
}

Each intent still holds one kind of a decision.

No mixing. No Guessing.

Using Arbitrary Values or Custom CSS

Shilp CSS does not invent a special syntax for everything. If something does not fit an intent, mixin or an utility, use plain CSS.

Intent-based styling is a tool, not a cage.

You can freely write:

Using custom CSS to build a component
.card {
	/* Regular intents */
	@text color-surface-fg;

	/* Plain CSS */
	background-image: linear-gradient(
		in oklch to top right,
		cyan 20%,
		magenta 50%,
		yellow 70%,
		black 100%
	);

	/* Stateful intents ( theme ) */
	@theme dark {
		@bg color-surface/90;
		@text color-surface-fg/90;
	}
}

Using Theme Values Inline

You can reference theme values directly in custom CSS using inline theme(...) function. You can use it anywhere a value is expected.

Using inline theme value for custom CSS
.any-class {
	background-image: linear-gradient(
		in oklch to theme(angles-45),
		oklch(theme(colors-html-cyan)) theme(spacing-1/5),
		oklch(theme(colors-html-magenta) / 0.9) theme(spacing-1/2),
		oklch(theme(colors-orange-600)) 70%,
		oklch(theme(colors-black)) theme(spacing-full)
	);
}

This keeps custom CSS aligned with the system without forcing everything through intent and utilities.

See: Inline Theme Function.

Using The Important Flag

Shilp CSS has !important flag implementation. It can be applied to intent and utilities, but not on mixins.

Add ! just after utility or intent name.

Using the important flag
.card {
	/* on one utility */
	@layout is-flex! shadow-sm;

	/* on all utilities */
	@layout is-flex! shadow-sm!;

	/* on all utilities but via intent name */
	@layout! is-flex shadow-sm;
}

This works. But, it should be rare.

Use it only when:

  1. Overriding third-party CSS
  2. Debugging

Conflicting Intent or Utilities

When multiple declarations apply, the rule with higher specificity wins.
For simplicity, ASSUME the last declration wins.

This follows normal CSS behavior. Shilp CSS does not add conflict resolution logic on top.

Because intent is grouped, conflicts are usually obvious and easy to fix.

If you are unsure which rule applies, check the browser’s DevTool.

See: Understanding Specificity

Why This Scales Better?

When intent is grouped:

  • Layout refactors don’t touch text rules
  • Changes don’t require markup edits
  • Shared patterns are obvious and named
  • The affected area is predictable

This is what makes large systems calm to maintain.

Is Shilp CSS Anti-Utility?

A big NO.

Utilities still exist. But, they are config-driven, reusable and composable.

The difference is where composition happens.

ApproachComposition
Utility-first atomic classesin markup
Shilp CSSin CSS

Shilp CSS keeps Intent and Utility-first idea both, and makes sure they live where they belong.

Same power. Different ownership model.

What You Intentionally Avoid

Styling with intent means you avoid:

  • Encoding meaning through loooong string of classes
  • Mixing unrelated concerns in one place
  • Relying on conventions to imply structure
  • Refactoring by hunting class strings

Intent is explicit by construction.

The Outcome

Styling with intent gives you:

  • Clear ownership of styles
  • Readable, navigable stylesheets
  • Semantic and smaller markup
  • Refactors that feel controlled

This is not about writing more CSS. It’s about writing CSS that explains itself.

Reserved Names

CSS at-rules:

  • @charset
  • @import
  • @namespace
  • @media
  • @supports
  • @layer
  • @scope
  • @container
  • @page
  • @font-face
  • @font-feature-values
  • @keyframes
  • @counter-style
  • @property
  • @color-profile
  • @view-transition
  • @starting-style

SCSS at-rules:

  • @use
  • @forward
  • @import
  • @mixin
  • @include
  • @function
  • @return
  • @if
  • @else
  • @each
  • @for
  • @while
  • @error
  • @warn
  • @debug
  • @at-root
  • @extend
  • @content