2.0.5 • Published 3 years ago

@glitz/type v2.0.5

Weekly downloads
91
License
MIT
Repository
github
Last release
3 years ago

Glitz

Glitz is a CSS-in-JS library that is strictly focused on:

:zap: Performance by caching and avoiding unnecessary re-renders whenever possible :policeman: Type safety by TypeScript :balance_scale: Lightweight @glitz/core bundle size (minified + gzip) by keeping things simple :muscle: Flexibility by composition to avoid wrapping elements :fire: Official React bindings

Along with other built-in features like:

  • Atomic CSS (and non-Atomic CSS)
  • Shorthand expansion
  • Pseudo selectors/elements
  • Fallback values
  • @media support
  • @keyframes support
  • @font-face support
  • Media query ordering
  • Server rendering
  • Vendor prefixing (with @glitz/transformers)
  • Number to unit conversion 10 -> "10px"(with @glitz/transformers)
  • Warnings and errors when things goes wrong in development (with @glitz/transformers)

Table of content

Getting started

$ yarn add @glitz/core @glitz/transformers

// or

$ npm install @glitz/core @glitz/transformers

The most basic implementation is really easy. You don't need any config or module loaders to get started.

import { GlitzClient } from '@glitz/core';
import transformers from '@glitz/transformers';
const glitz = new GlitzClient({ transformer: transformers() });

const className = glitz.injectStyle({
  color: 'green',
});

Features

Atomic

Each declaration will be injected individually by default which means that declaration blocks are divided into as small atomic rules as possible before they are injected into a virtual style sheet using the CSSStyleSheet.insertRule() method. This results in minimal output and maximum performance because each class becomes highly reusable.

const className = glitz.injectStyle({
  display: 'flex',
  color: 'green',
  // Will be injected as:
  // .a {
  //   display: flex;
  // }
  // .b {
  //   color: green;
  // }
});

So the next time you use display: 'flex' it will reuse a instead of injecting a new rule.

However, the side-effect of this is that you cannot guarantee the order of the CSS. That why it's recommended to always use longhand properties. More info about using shorthand properties here. You're able to disable this feature but it's not recommended.

Pseudos

You define your pseudo selector or element as the property name, followed with the style you wish to have for that pseudo.

const className = glitz.injectStyle({
  ':hover': {
    textDecoration: 'underline',
    // You're also able to nest
    ':after': {
      content: '"Don\'t forget double quotes when doing this"',
    },
  },
});

Fallback values

An array of values will be injected as one rule.

const className = glitz.injectStyle({
  width: ['50%', 'fit-content'],
  // Will be injected as:
  // .a {
  //   width: 50%;
  //   width: fit-content;
  // }
});

Keyframes

The animationName property injects the @keyframes declaration list and will be replaced by a unique name.

const className = glitz.injectStyle({
  animationName: {
    // or animation: { name: { ...
    from: {
      color: 'red',
    },
    to: {
      color: 'green',
    },
  },
  // Will be injected as:
  // .a {
  //   animation-name: a;
  // }
  // @keyframes a {
  //   from {
  //     color: red;
  //   }
  //   to {
  //     color: green;
  //   }
  // }
});

The name will be reused when an identical declaration list is used again.

Font faces

The fontFamily property injects the @font-face rule and will be replaced by a unique name.

const className = glitz.injectStyle({
  fontFamily: {
    // or font: { family: { ...
    fontStyle: 'normal',
    fontWeight: 400,
    src: "url(https://domain.tld/path/to/font.woff2) format('woff2')",
  },
  // Will be injected as:
  // .a {
  //   font-family: a;
  // }
  // @font-face {
  //   font-style: normal;
  //   font-weight: 400;
  //   src: url(https://domain.tld/path/to/font.woff2) format('woff2');
  //   font-family: a;
  // }
});

The font family name will be reused when an identical block is used again.

You're also able to use fallback values in combination with font faces.

const className = glitz.injectStyle({
  fontFamily: [
    {
      fontStyle: 'normal',
      fontWeight: 400,
      src: "url(https://domain.tld/path/to/font.woff2) format('woff2')",
    },
    'sans-serif',
  ],
});

Media queries

You can define any @media property as you like.

const className = glitz.injectStyle({
  '@media (min-width: 768px)': {
    display: 'block',
  },
});

React

The official React bindings for Glitz are highly flexible and composable.

Server rendering

The difference between GlitzServer class and GlitzClient class is that GlitzClient inserts new rules into the DOM directly. Instead GlitzServer collects the rendered style as a string for you to put in the <head>. The client side will then hydrate the CSS and reuse it.

Shorthand properties

Problems mixing CSS shorthand and longhand properties are common with styling techniques like this and doesn't only affects Glitz. It often causes unexpected behaviors.

const first = glitz.injectStyle({
  marginLeft: 10,
});

// Bad
const second = glitz.injectStyle({
  margin: 20,
  marginLeft: 10, // <- The order of the CSS will result in this never being applied as expected
});

// Good
const second = glitz.injectStyle({
  marginTop: 20,
  marginRight: 20,
  marginBottom: 20,
  marginLeft: 10,
});

Instead of writing each longhand property separately, you're able to use objects with shorthand properties.

// Good
const second = glitz.injectStyle({
  margin: {
    top: 20,
    right: 20,
    bottom: 20,
    left: 10,
  },
});

For margin, padding and border there's some property aliases to make this easier:

  • x is an alias for left and right
  • y is an alias for top and bottom
  • xy is an alias for left, right, top and bottom
// Bad
const second = glitz.injectStyle({
  padding: '10px 20px',
  border: 'red solid 5px',
});

// Good
const second = glitz.injectStyle({
  padding: {
    y: 10,
    x: 20,
  },
  border: {
    xy: {
      color: 'red',
      style: 'solid',
      width: 5,
    },
  },
});

You can see a complete list of shorthand objects here.

TypeScript

You don't need TypeScript to use Glitz. But if you do, everything is typed! Even CSS! This means you'll get autocompletion and warnings as you write.

const className = glitz.injectStyle({
  colour: 'white', // Type error on property
  overflow: 'hide', // Type error on value
});

Unknown properties

Unknown properties will fail to be able to notify you when there's a typo. This means that function-like pseudos (e.g. :not(:first-child)) and media query selectors will be considered unknown properties. For those, there's two helper functions (pseudo and media) that will make the selectors valid.

import { media, pseudo } from '@glitz/core';

const className = glitz.injectStyle({
  ...media(
    { minWidth: '768px' },
    {
      display: 'block',
    },
  ),
  ...pseudo(':not(:first-child)', {
    textDecoration: 'underline',
  }),
});

Add custom properties

You can also extend the interface with custom CSS properties like CSS variables and other unknown properties using module augmentation.

// my-style.d.ts
import * as Glitz from '@glitz/type';

declare module '@glitz/type' {
  interface Properties {
    // Add CSS property
    mozOsxFontSmoothing?: string;

    // Add CSS variable name
    '--theme-color'?: string;

    // Allow any other property
    [property: string]: any;
  }
}

Transformers

Styles will be processed by transformers before they are injected. A transform function will receive a flat object with string | number | Array<string | number> values and expects the same in return. Have in mind that the transformer will receive each unique declaration only ones. The same unique declaration will later use a cached result and will never again reach the transformer.

These are not built in by default because it gives the users the freedom of choice and makes it easier to adopt to other platforms in the future.

All

The @glitz/transformers module includes all official transformers:

import { GlitzClient } from '@glitz/core';
import transformers from '@glitz/transformers';
const glitz = new GlitzClient({ transformer: transformers() });

Prefixer

The @glitz/prefixer-transformer is basically just a TypeScript wrapper for inline-style-prefixer/static.

import { GlitzClient } from '@glitz/core';
import prefixer from '@glitz/prefixer-transformer';
const glitz = new GlitzClient({ transformer: prefixer });

const className = glitz.injectStyle({
  display: 'flex',
  // Will be transformed into:
  // {
  //   display: [
  //     '-webkit-box',
  //     '-moz-box',
  //     '-ms-flexbox',
  //     '-webkit-flex',
  //     'flex',
  //   ],
  // }
});

Number as length

The @glitz/length-transformer converts numbers to lengths for certain properties.

import { GlitzClient } from '@glitz/core';
import numberToLength from '@glitz/length-transformer';
const glitz = new GlitzClient({ transformer: numberToLength });

const className = glitz.injectStyle({
  height: 10,
  width: [100, 'max-content'],
  // Will be transformed into:
  // {
  //   height: '10px',
  //   width: ['100px', 'max-content'],
  // }
});

DevTool

The @glitz/devtool-transformer produces warnings and errors when something does wrong in development.

import { GlitzClient } from '@glitz/core';
import devTool from '@glitz/devtool-transformer';
const glitz = new GlitzClient({ transformer: devTool });

const className = glitz.injectStyle({
  width: 'calc(100)',
  // Will warn that `width` was ignored by the browser due to an error (unit missing)
});

API

new GlitzClient(options?: Options)

The Glitz core class for browsers.

Method injectStyle(style: Style)

Returns: string

The returned value contains the class names of the injected style.

new GlitzServer(options?: Options)

The Glitz core class for servers.

Method injectStyle(style: Style)

Returns: string

Class names of the injected style.

Method getStyleMarkup()

Returns: string

Markup with style sheets to render into <head> that the Glitz core class for browsers will reuse.

Options

options.identifier

identifier: string;

Default: "glitz"

The dataset name that will be used to identify Glitz style elements.

options.transformer

transformer(style: Properties): Properties

Default: undefined

Styles will be processed by transformers before they are injected. A transform function will receive a flat object with string | number | Array<string | number> values and expects the same in return. Have in mind that the transformer will receive each unique declaration only ones. The same unique declaration will later use a cached result and will never again reach the transformer.

Official transformers are:

To use all the official transformers, use @glitz/transformers:

import { GlitzClient } from '@glitz/core';
import transformers from '@glitz/transformers';
const glitz = new GlitzClient({ transformer: transformers() });

options.mediaOrder

mediaOrder(a: string, b: string): number

Default: undefined

Unordered media style may sometimes cause some unwanted behavior. With this function you're able to sort the order of the injected media styles.

It's recommended that you create your own with the media queries you use.

import { query } from '@glitz/core';

const mediaQueryOrder = [
  query({minWidth: '320px'}),
  query({minWidth: '768px'}),
  ...
];

function mediaQuerySorter(a, b) {
  const indexA = mediaQueryOrder.indexOf(a);
  const indexB = mediaQueryOrder.indexOf(b);
  return indexA - indexB;
}

const glitz = new GlitzClient({ mediaOrder: mediaQuerySorter });

It's also possible to use sort-css-media-queries if you don't have a specific list of media queries.

options.atomic

atomic: boolean;

Default: true

Breaks down each CSS declaration to separate class names for minimal output and maximum performance. This can cause problems if you e.g. mix longhand and shorthand properties because the order of the CSS can't be guaranteed. Disabling this isn't recommended, but possible by setting this to false.

options.prefix

prefix: string;

Default: ""

Prefix all class names.

Helpers

pseudo

pseudo(selector: string, style?: Style): Style

Validates the pseudo rule. See example.

media

media(query: Query | string, style?: Style): Style

Parse and validate Query or string into a valid media rule. See example.

query

query(query: Query): string

Parse and validate Query into a valid media query.

Playground

To play around with Glitz, just:

$ git clone https://github.com/frenic/glitz.git
$ cd glitz
$ yarn install
$ yarn example

Open http://localhost:1234 in your browser and edit the code in packages/example.

3.0.0-alpha.8

3 years ago

3.0.0-alpha.7

4 years ago

2.0.5

4 years ago

2.0.4

4 years ago

3.0.0-alpha.6

4 years ago

3.0.0-alpha.5

4 years ago

3.0.0-alpha.4

4 years ago

3.0.0-alpha.3

4 years ago

3.0.0-alpha.2

4 years ago

3.0.0-alpha.1

4 years ago

3.0.0-alpha.0

4 years ago

2.0.3

4 years ago

2.0.2

5 years ago

2.0.1

6 years ago

2.0.0

6 years ago

2.0.0-alpha.3

6 years ago

2.0.0-alpha.2

6 years ago

2.0.0-alpha.1

6 years ago

2.0.0-alpha.0

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.2-beta.0

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.1.0-beta.12

6 years ago

1.1.0-beta.11

6 years ago

1.1.0-beta.10

6 years ago

1.1.0-beta.9

6 years ago

1.1.0-beta.8

6 years ago

1.1.0-beta.7

6 years ago

1.1.0-beta.6

6 years ago

1.1.0-beta.5

6 years ago

1.1.0-beta.4

6 years ago

1.1.0-beta.3

6 years ago

1.1.0-beta.2

6 years ago

1.1.0-beta.1

6 years ago

1.1.0-beta.0

6 years ago

1.0.0

6 years ago

1.0.0-beta.4

6 years ago

1.0.0-beta.3

6 years ago

1.0.0-beta.2

6 years ago

1.0.0-beta.1

6 years ago

1.0.0-beta.0

6 years ago