npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dashkite/quark

v1.3.0-beta.8

Published

Combinators for generating CSS

Downloads

68

Readme

Quark

Combinators for generating CSS

Preprocessors, scoped CSS, atomic CSS, and CSS in JS are among the many ways developers and designers have tried to encapsulate, reuse, and manage CSS. Quark represents a new approach: encapsulating CSS in functions, which may then be composed into stylesheets.

css = render styles [
  select "main > article", [
    reset "block"
    margin "bottom left"
    article [ "h1", "p", "lists", "blockquote", "figure" ] ] ]

This will produce CSS that looks like the following:

main > article {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
main > article > h1 {
  font-family: 'sans-serif';
  font-weight: 'bold';
  font-size: '6.4rem';
  line-height: '8rem';
  color: '#111';
}
main > article > p {
  /* ... and so on  */

Obviously, the Quark code doesn’t look much like CSS, but that’s the point: we encapsulate resuable CSS as functions. In turn, those functions are composed to build up more sophisticated functions. As with Atomic CSS, we start out with simple building blocks. But unlike Atomic CSS, our building blocks are composable functions.

Let’s start with the building blocks. We have three:

  • styles creates a new stylesheet.
  • select places a new selector on the stack.
  • set sets a property.

We also rely on Garden’s pipe and pipeWith functions for composition and Katana to manage our context stack. However, most of the time, you won’t need to worry about them.

With these simple functions, we can already build up arbitrarily complex stylesheets. But it’s a bit tedious, so we define simple combinators that are the Quark equivalent of atomic CSS. For example, this is bold:

bold = set "font-weight", "bold"

This is plain, which provides a simple illustration of composition :

plain = pipe [
  set "font-style", "normal"
  set "font-weight", "normal"
  set "text-decoration", "none"
  set "text-transform", "none"
]

The set function is curried, so we can also just provide shortcut functions for setting a property:

mt = set "margin-top"

which we can later use in our stylesheet definitions like this:

mt "4rem"

or to build up new combinators, like this:

# margin-bottom large
mtl = mt "4rem"

If we want to be less cryptic, we can introduce lookup tables. The lookup helper makes this easy:

margin = lookup
  "top large": set "margin-top", "4rem"
  "top medium": set "margin-top", "2rem"
  "top small": set "margin-top", "1rem"
  # ... and so on

We can now use margin like this:

margin "top large"

Sometimes you simply want to lookup a value, like a color, and set a property using that value. We can do this using pipe, lookup, and any, which tries functions until one returns a defined value. This example tries a lookup for predefined color values, falling back to the value passed into it:

# colors is a lookup table for color values
color = pipe [
  any [
    lookup colors
    identity
  ]
  set "color"
]

Sometimes you want to do a bunch of lookups and compose them together. We can do this with Garden's pipeWith:

article = pipeWith lookup {h1, h2, p, ul, ol, li, blockquote}

which allows us to write:

aritcle [ "h1", "p" ]

Of course, since these are still just functions, we can simply write them out when we need something funky. Here’s a combinator that allows us to express the font size relative to the line-height:

text = curry (lh, r) ->
  pipe [
    set "line-height", lh
    set "font-size", "calc(#{lh} * #{r})"
  ]

Similarly, themes are just a set of combinators. Since it’s tedious to pass theme functions to every combinator that might need it, we can bind an entire set at once. Given a collection of related combinators, we can provide a bind function:

{article} = Article.bind {color, type}

The combinators returned from the bind function will now use our theme functions.

Combinators may introduce nested selectors, which allows us to go beyond collections of properties. For example, the Article H1 combinator we referenced in our example introduces a nested selector:

h1 = tee pipe [
  select "> h1", [
    type "heading"
    color "near-black"  
  ]
]

We use tee here so that subsequent combinators won’t end up being applied to our H1 selector.

API

Core

styles

Create a new stylesheet.

select

Push a selector onto the stack.

set

Set a property for a given rule.

lookup

Takes a dictionary and returns a function that will return the result of dereferencing its argument using the dictionary.

margins = lookup "none": "none", "small": "1rem", "medium": "2rem", "large": "3rem"
assert.equal "2rem", margins "medium"

Since this is a curried function, you can use it define simple presets:

type = lookup
  heading: pipe [ sans, bold, text (rem 8), 4/5 ]"

any

Returns a function that, given a value, will apply each given function with that value until one returns a value that isn’t undefined. In combination with lookup, this allows you to implement common patterns like prefixed properties:

width = any [
  lookup
    stretch: pipe [
      set "width", "-webkit-fill-available"
      set "width", "stretch"
  ]
  # fallback...
  set "width"
]

toString

Given a stylesheet object, returns CSS.

render

Given a stylesheet function, executes it and calls toString.

assert.equal render styles [ select "main", [ width "90%" ] ],
	"main { width: 90%; }"

Units

px, pct, em, rem, vh, vw

Converts a number into CSS units.

# set width to 20rem
width rem 20

Height and Width

width, height

Set with units, or other CSS value, ex: min-content. Prefixes stretch.

width pct 1/3
width "fit-content(20rem)"

min, max

Modifies width or height.

# set the max width
max width "34em"

readable

Sets the width, min-width, and max-width to ensure the readability of the text. Equivalent to:

width "stretch"
min width "20em"
max width "34em"

Based on Tachyon’s measure classes.

Color

color

Sets the color property with color name based on the HTML or Tachyon color.

color "white-20"

background

Sets the background property with a color name based on the HTML or Tachyon color or any valid background shorthand.

Typography

italic, bold, underline, strikeout, capitalize, uppercase, plain

Sets the corresponding text property.

sans, serif, monospace

Sets the font family.

text

Sets the line-height and relative font-size.

# set the line-height to 8rem and the font-size to 5.33rem
text (rem 8), 2/3

type

Sets font/text properties based on standard presets.

type "extra large heading"

Presets:

| Preset | Line Height (rem) | Font Size (rem) | Font Size (px) | | ------------------- | ----------------- | --------------- | -------------- | | banner | 18 | 14.4 | 96 | | extra large heading | 14 | 11.2 | 74 2⁄3 | | large heading | 10 | 8 | 53 1⁄3 | | heading | 8 | 6.4 | 42 2⁄3 | | small heading | 7 | 5.6 | 37 1⁄3 | | extra small heading | 6 | 4.8 | 32 | | extra large copy | 8 | 5⅓ | 35 5⁄9 | | large copy | 7 | 4⅔ | 31 1⁄9 | | copy | 6 | 4 | 26 2⁄3 | | small copy | 5 | 3⅓ | 22 2⁄9 | | extra small copy | 4 | 2⅔ | 17 8⁄9 |

Pixel values are based on a rem size of 6 2⁄3 pixels.

Borders

border

Sets the border using an array of presets. Color will be inherited unless set with borderColor.

border [ "round" ]
border [ "top", "bottom" ]
border [ "pill" ]
Presets
  • top, bottom, left, right
  • dotted, dashed, thin, thick, thicker, thickest
  • box, round, rounder, pill
  • any value accepted by color

Background Size

cover, contain

Sets the background-size property.

Opacity

opacity

Set the opacity using a percentage.

visible

Set the opacity to 100%.

invisible

Set the opacity to 0%.

Flexbox

rows, columns

Set up a row or column-based container.

wrap

Sets a flex container to wrap content. (Not wrapping is the default.)

Forms

Presets for a variety of form elements:

  • h1
  • section
  • input

Tables

Code

Resets

Presets for a variety of resets:

  • body, block, list, blockquote, table

Normalize

normalize

Article

Article.bind

article

  • [ ]
    • [ ]