Skip to content

Loci

State-based styles

Loci is a low-level CSS framework that is centered around state.

npm i locicss

Start with the state

Rethink your styles by considering state-first.

<input
  type="text"
  placeholder="Text input styled with Loci"
  data-style
  data-:hover
  data-:focus
  data-:active
  data-::placeholder
/>

Set your variables

Set colors, spacing, and more with CSS custom properties. In Loci, every value is a custom property.

const colors = {
  red: {
    50: #f00,
    100: #f00,
    200: #f00,
    300: #f00,
    // …
  }
}
:root {
  --loci-red-300: #f00;
  --loci-space-8: 8px;
}

Use utility styles

Use single-purpose, utility style declarations with your defined variables. Verbose, understandable.

<input
  type="text"
  placeholder="Loci"
  data-style="width:100% border-color:blue-500 padding:16 font-size:20 color:blue-900 background-color:blue-100"
  data-:hover="background-color:blue-200"
  data-:focus="background-color:blue-200"
  data-:active="background-color:blue-300"
  data-::placeholder="color:blue-600"
/>

Define keywords that work for your system

Prefer writing p instead of padding or bg instead of background-color? Set your alternative property names in your configuration file.

{
  [`alternativePropertyNames`]: {
    [`background-color`]: `bg`,
    [`margin-bottom`]: `mb`,
    [`margin-left`]: `ml`,
    [`margin-right`]: `mr`,
    [`margin-top`]: `mt`,
    [`margin`]: `m`,
    [`padding-bottom`]: `pb`,
    [`padding-left`]: `pl`,
    [`padding-right`]: `pr`,
    [`padding-top`]: `pt`,
    [`padding`]: `p`,
  }
}
<input
  type="text"
  placeholder="Loci"
  data-style="width:100% border-color:blue-500 p:16 font-size:20 color:blue-900 bg:blue-100"
  data-:hover="bg:blue-200"
  data-:focus="bg:blue-200"
  data-:active="bg:blue-300"
  data-::placeholder="color:blue-600"
/>

Use classes for grouped component state styles

Use utility styles to define grouped component styles with a class.

{
  [`components`]: {
    [`button`]: {
      [`style`]: `appearance:none bg:red-300 border-color:blue-400 color:red-800 pt:8 pb:8 pr:16 pl:16`,
      [`:hover`]: `bg:red-400 color:red-900`,
      [`:focus`]: `bg:red-400 color:red-900`,
      [`:active`]: `bg:red-500`,
    }
  }
}
<button class="button">Press</button>

Variants

Extend components with variant grouped styles.

const variants: Variants = {
  [`button:primary`]: {
    [`style`]: `bg:blue-300 border-color:blue-600 color:blue-800`,
    [`:hover`]: `bg:blue-400 color:blue-900`,
    [`:focus`]: `bg:blue-400 color:blue-900`,
    [`:active`]: `bg:blue-500`,
  }
}
<button class="button" data-variant="button:ghost">Press</button>
<button class="button" data-variant="button:primary">Press</button>
<button class="button" data-variant="button:danger">Press</button>

Dynamic ::before and ::after content

<p
  data-style="mt:0 mb:0"
  data-::before-content="✅"
  data-::after-content=" (Or at least tried my best.)"
  data-::after="color:amber-400"
>
  I have read it all.
</p>

Scoped CSS variables

Change variables in scoped elements.

<div data-style="p:16 bg:blue-900 color:red-300">
  <h2 data-style="mt:4 font-family:sans-serif">Section title</h2>
  <p>Section description with the same text color.</p>
  <blockquote
    data-style="color:red-300 ml:0 p:8 border-style:solid"
    data-custom-property="--loci-red-300:limegreen"
  >
    Block quote with a different text color.
  </blockquote>
  <p>Section description continued.</p>
</div>

Improve accessibility

Add styles based on aria states and roles.

<input
  aria-invalid
  data-aria-invalid="border-color:red-600"
>

Developer mode

Display a banner when your app is using CSS that is not purged for production.

const config: Config = {
  choices: {
    base: [`theme`, `developerMode`],
  },
}

See developer mode banner at the bottom of this page.

Remove magic

Styles are deliberately applied per state and leverage the familiar HTML + CSS API.

User-defined custom states

Allows the user to define their own states, like `data-task-completed`.

This may have been addressed in another section.

Error handling and debugging

Is it possible to warn a user if two of the same property are applied to the same state? For example:

<div data-:hover="bg:blue-100 bg:blue-200">…</div>

Globals

* { box-sizing: border-box; } *::selection { background-color: var(--blue-100); } For example:

<div data-global="bg:blue-100">…</div>

Or, should these be accomplished with scoped variables?

<div data-custom-property="--loci-selection-background-color:blue-100">…</div>

Auto-prefixing

Ensure styles are applied in all browsers that support them, and gracefully degrade to legacy browsers.

See what you're using

Display all property and values in use. (This is already available in the config file. 🤔)

Alternative value names

Roll a custom framework that works for you.

<div data-style="padding:0.25rem">…</div>
<div data-style="p:0.25rem">…</div>
<div data-style="p:s">…</div>
<div data-style="padding-right:auto padding-left:auto">…</div>
<div data-style="px:auto">…</div>
<div data-style="px:a">…</div>