postcss-omnicss v0.8.2
OmniCSS
Features to do
- Prevent creating nonsense classes for common class names:
content-container
,page-inner
, etc. - Decide what to do about semi-colons in class names
- Shorthand property inference:
bg-cover
should becomebackground-size: cover
instead ofbackground: cover
object-fit
andobject-position
- Custom
inset
shorthand property to settop
,right
,bottom
, andleft
(similar tomargin
orpadding
) mx
,my
,px
andpy
shorthands for setting horizontal/vertical padding/margin.- Greedy matching for variables in certain cases to allow for e.g.
color-$gold-300
@apply
directive for using applying utility classes in CSS- or even an
@omnicss
block
- or even an
- Namespaced CSS variables: variable name of
--padding-small: 1rem
should be able to used byp-$small
- Or
--transform-center: translate(-50%, -50%)
astransform-$center
- Or
- Some sort of debug mode so you can figure out why a selector doesn't work.
- Pug template parsing
- Better / more performant parsing of files specifically geared for extracting class strings
- Caching parse results to avoid reparsing files that haven't changed.
- Change
!first
modifier to use> * + *
because of it's low specificity - @apply to css (and sass) converter
- css to omnicss translator
- Set up documentation framework
hover:
classes should be wrapped in a@media (hover)
block by default (with option to turn off)- nth-child selectors
- accept
@screen
as an alias for@breakpoint
- add
border-top-radius
,border-bottom-radius
, etc. custom properties and utility classes - add
rounded
as an alias forborder-radius
- property default values:
padding
would becomepadding: var(--padding)
- needs to deal with abbreviations:
p
would still becomepadding: var(--padding)
- needs to deal with abbreviations:
- default property values from tailwind injected as CSS variables
- e.g.
--transition-property: background-color, border-color, color, fill, stroke, opacity, box-shadow, transform
- e.g.
- explore ways to achieve
group-hover
selectors white-space-nowrap
should be less picky about it's hyphenation- support for variables that start with a number, i.e.
font-size-$3xs
- add
z
as a shorthand forz-index
- custom units:
margin-10$x--$y
would becomemargin: calc(10 * var(--x)) calc(-1 * var(--y))
motion-reduce
andmotion-safe
support- use
$
syntax in CSS for variable declaration - explore Tailwind compatibility mode that makes tailwind classes just work in OmniCSS
- add way to generate modified versions of user-defined classes
- Tailwind does this by wrapping the classes in an
@responsive
block - we could even do this for all classes by default (could be slow)
- Tailwind does this by wrapping the classes in an
- desktop breakpoint should exclude iPad (i.e. >=768px and mobile would be <768px)
- add
uppercase
as a short-hand fortext-transform-uppercase
- add
ch:
as a short-hand forchild:
- nesting modifiers: e.g.
child:child:
- change
first:
to not be a child selector (have same behaviour as CSS:first-child
).- current behaviour would be attained with
first:child:
- same with
last:
- current behaviour would be attained with
- modifier order: e.g.
hover:child:
should have different behaviour tochild:hover:
not
(or!
) should work with any pseudo class modifier: e.g.!hover:
becomes:not(:hover)
- add a
fixed
custom property to matchabsolute
Housekeeping tasks
- Commenting
- Pull out data objects and data-like functions to seperate files
- Prefer snake_case variable and functions names
- Refactor features.test.js to a better format
- Possibly pull out some reused test functions
Bugs
absolute
custom property should have a lower specificity thanright
,left
,transform
, etc.- Same with
flexbox
and it's dependent properties (not sure if I did this)
- Same with
opacity-50%
becameopacity: 1%
when building for production using webpack and postcss-loadercol-span-3
becomescolumn-span: 3
and notgrid-column: span 3
-rotate--10deg
converts to--rotate-: 10deg;
Talking points
- Intro
- Promotes an inline CSS style where you can write your templates and your styles at the same time in the same place
- Generates convention-based utility classes based on the class strings found in your template files
- A basic fully worded example
- Inspired by TailwindCSS and PurgeCSS, but with some crucial differences.
- Little to no configuration
- Supports the full set of CSS declarations, such as shorthand properties, CSS grid, filters, transforms, etc.
- Allows the use of the most commonly reached for CSS features, such as media queries, hover & focus selectors, child selectors.
- Supports the use of CSS custom properties and calculations, i.e.
var()
andcalc()
Getting Started
Using PostCSS CLI
npm install --save-dev postcss-omnicss postcss postcss-cli
- Create (or locate) your source CSS file. For this example, the source file will be located in
src/app.css
. - Add the
@omnicss
directive to the source CSS file. It is generally recommended to be placed at the end of your file, so that the omnicss classes can be used to override properties in your other classes. Create (or locate) your source template files. This can be any number of .html, .js, .vue or .jsx files. Other template file types will work, as long as class strings appear as single or double-quoted, space-separated strings. For this example, the source file will be
index.html
with the following content:<html> <head> <link rel="stylesheet" href="/dist/app.css"> </head> <body> <p class="bg-red color-white"> I will have a red background and white text </p> </body> </html>
Note: The stylesheet link has a href of
/dist/app.css
, which is where we are going to instruct PostCSS CLI to output our CSS file after building.Add the plugin to PostCSS by creating a
postcss.config.js
file in the project's root folder and adding the following content:
module.exports = { plugins: [ require('postcss-omnicss'), ] }
- Run postcss using:
npx postcss src/app.css -o dist/app.css
, wheresrc/app.css
is the path to your source CSS file. This will output adist/app.css
from your source CSS file with the@omnicss
directive replaced by the OmniCSS utility classes. With the exampleindex.html
file above, the output CSS file will be:
.bg-red { background: red } .color-white { color: white }
As you can see this would style the
p
element with a red background and white text color
How to turn a CSS declaration into an OmniCSS class
Important features
Modifiers
- The main benefit that OmniCSS has over using inline style attributes.
- You can take any OmniCSS class and add modifiers to it.
- Modifiers precede the class, each ending with a colon (
:
) - Responsiveness
- For example, if you only want
display-flex
to be applied on mobile screen sizes, simply addmobile:
to it, becomingmobile:display-flex
. Or using an abbreviation,m:display-flex
. - By default, mobile screen sizes corresponds to a max screen width of 768px, but this can be configured.
- Similarly, classes can be applied only on desktop by prepending
desktop:
(or simplyd:
) e.g.desktop:flex-direction-column
. - Modifiers for other breakpoints are available, or you can add your own.
- For example, if you only want
- Pseudo-classes
- Some CSS pseudo-classes are also OmniCSS modifiers.
- These should be pretty intuitive:
hover:
styles the element when it is hovered by the mouse, whereasfocus:
applies when it is focused.placeholder:
styles the placeholder of an input element.before:
andafter:
styles the before or after pseudo-elements.
Child selectors
- Another common scenario is when you would like to style all of the direct children of an element. Rather than creating a traditional CSS class and applying to each child, you can use the
child:
modifier. For example,
child:margin-top-1rem
would generate the following CSS:child\:margin-top-1rem > * { margin-top: 1rem }
first-child:
(or simplyfirst:
) will style just the first direct child of the element, whereaslast-child:
(or simplylast:
) will only style the last child.not-first-child:
(ornot-first:
or!first:
) will style all children but the first, this is useful for adding spacing between elements, e.g.not-first-child:margin-bottom-1rem
(or!first:mb-1
). Similarly,not-last-child
(ornot-last:
or!last:
) will style all children but the last.
- Another common scenario is when you would like to style all of the direct children of an element. Rather than creating a traditional CSS class and applying to each child, you can use the
Important values
important:
or!:
makes the property value important. e.g.!:color-black
becomescolor: black !important
- Combining modifiers
- Multiple modifiers can be used to combine their effects, e.g.
mobile:hover:color-black
. - Their order does sometimes matter.
- The responsiveness modifiers can be interchanged with other modifiers. For example,
mobile:hover:
is equivalent tohover:mobile:
. - But the order of pseudo-selectors and child selectors matters. For example,
child:hover:
will style all the children of a hovered element, whereashover:child:
will style the children when they are individually hovered.
- The responsiveness modifiers can be interchanged with other modifiers. For example,
- Multiple modifiers can be used to combine their effects, e.g.
CSS Variables (Custom properties)
- Usage
- You can both set and use the value of CSS variables in OmniCSS classes.
- For example,
--standard-margin-10rem-5rem
becomes--standard-margin: 10rem 5rem
- And
margin-var(--standard-margin)
becomesmargin: var(--standard-margin)
- You can define fallback values as normal.
- It should be noted that there is no default unit for CSS variables.
- Shorthand
- As this syntax can be a little unweildly, we have provided a consistent shorthand for both cases using the
$
symbol. - The above example would become
$standard-margin-10rem-5rem
to set the variable, andmargin-$standard-margin
to use the variable. - You can leave out the brackets (in most cases)
- You can define fallback values with the shorthand, but you can't leave out the brackets if you do.
- As this syntax can be a little unweildly, we have provided a consistent shorthand for both cases using the
Use cases
Declaring common values (colors, fonts, etc.)
- You can declare commonly used literal values used throughout your code base.
- Useful for colors and font families.
Rather than setting these inline, they can be set in your main CSS file. e.g.
:root { /* colors */ --red: #e51937; --yellow: #fff600; --purple: #b400ff; /* fonts */ --sans: "Open Sans", sans-serif; --mono: "Roboto Mono", monospace; /* transitions */ --duration: .3s; --easing: cubic-bezier(.9, 0, .1, 1); /* transforms */ --center: translate(-50%,-50%); /* filters */ --drop-shadow: drop-shadow(0 0 20px rgba(50,7,68,.45)); }
Then they can be used anywhere in your code base as follows:
color-$purple
background-color-$red
(orbg-color-$red
or evenbg-$red
)box-shadow-3px-3px-5px-$yellow
font-family-$mono
transition-transform-$duration-$easing
transform-$center-rotate(45deg)
filter-$drop-shadow-blur(5px)
Setting one property based on another
- Sometimes our properties are related to one another and it's nice to be able to express that explicitly.
- For example, the following OmniCSS classes would make a square element with a width and height of
10rem
:$width-10rem width-$width height-$width
- This declares a variable
$width
equal to10rem
and then sets the width and height to that value. - The advantage of this is that we can change the width later without remembering to also change the height to match.
- For example, we can change the width and height of the element to
5rem
by adding the following class:m:$width-5rem
- With shorthands, this becomes:
$w-10rem w-$w h-$w
- This can be used in combination with calc() to create different aspect ratios.
- For example, changing the previous example to a 16:9 box would be as simple as follows:
$width-10rem width-$width height-calc(9/16*$w)
- Or with shorthands, this becomes
$w-10rem w-$w h-(9/16*$w)
Splitting transforms into seperate properties
- Sometimes we need to be able to change only part of our transforms without changing the rest
- This same technique can be useful for other compound properties that lack separate properties, such as filter.
- Let's say that we have a complicated transform:
transform-rotate(45deg)-skew(30deg,25deg)-translate(40%,50%)
- But on mobile we need to change the x-value of translate:
m:transform-rotate(45deg)-skew(30deg,25deg)-translate(70%,50%)
Rather than repeating ourselves, we can use a variable as follows:
transform-rotate(45deg)-skew(30deg,25deg)-translate($translate-x,50%) $translate-x-40% m:$translate-x-70%
Or using a fallback:
transform-rotate(45deg)-skew(30deg,25deg)-translate($(translate-x,40%),50%) m:$translate-x-70%
- Usage
Set up and usage
- @omnicss
- PostCSS config
- Laravel Mix
- Configuration options
- Reference
- Responsiveness and breakpoints
- CSS Variables
- calc()
- calc() functions can be used in OmniCSS classes, i.e.
left-calc(50%-3rem)
- As a shorthand, the calc keyword can actually be omitted, simply leaving the round brackets, i.e.
left-(50%-3rem)
- Nested brackets in calc() function as expected:
width-((100%-2rem)/3)
- CSS variables can be used in conjunction with calc():
left-(50%-$left-offset)
becomesleft: calc(50% - var(--left-offset))
- calc() functions can be used in OmniCSS classes, i.e.
- Shorthand properties
- You can use any of CSS's shorthand properties, such as
margin
,padding
,background
,border
,transition
,animation
, and evengrid
. - Some examples
- You can use any of CSS's shorthand properties, such as
- Numbers and units
- You can use any unit you like, as well as negative and floating-point numbers.
- This means that you can use
%
,vh
,vw
,vmin
,em
,px
, etc. - The leading-zero on floating point numbers is optional.
- Default units
- If you omit the unit for a property that requires one, we will add a default one.
- For most properties this is
rem
but the fill list can be found below.
- Abbreviations
- Provides some abbreviations for common properties, e.g. instead of writing
padding-1rem
, you could writep-1rem
, or instead ofpadding-left-1rem
, you could writepl-1rem
. - This can be used in combination with default units to make your classes even shorter. e.g. the default unit for
width
isrem
, so instead of writingwidth-10rem
, you could writew-10
. - All abbreviations are optional. The same styles will be applied to both the abbreviated and un-abbreviated forms.
- If OmniCSS finds multiple classes that resolve to the same CSS declaration, they will be combined into a single rule to prevent bloating the output CSS file.
- Provides some abbreviations for common properties, e.g. instead of writing
- Prefixed properties
- Pseudo-classes
active:
,any-link:
,blank:
,checked:
,default:
,disabled:
,empty:
,enabled:
,focus:
,focus-visible:
,focus-within:
,host:
,hover:
,indeterminate:
,in-range:
,invalid:
,link:
,optional:
,out-of-range:
,placeholder-shown:
,read-only:
,read-write:
,required:
,target:
,valid:
,visited:
Technical details
Specificity issues
- Most OmniCSS rules are a single class selector and have a specificity of 10
- Therefore, order that they appear in the CSS file matters
- More specific properties are output after less-specific ones, e.g.
border
classes will appear beforeborder-left
, which will appear beforeborder-left-color
- This is so you can use shorthand properties, then override them with more specific cases, e.g.
margin-1rem margin-bottom-2rem
- Classes with responsive modifiers are output in their media query after their non-responsive counterparts.
Child selectors versions of classes are output before their normal versions. This is so that you can override inherited child styles on the element directly:
<div class="display-flex child:flex-basis-20%"> <div></div> <div class="flex-basis-40%"></div> <div></div> <div></div> </div>