npm.io
2.1.19 • Published 2 months ago

goober

Licence
MIT
Version
2.1.19
Deps
0
Size
111 kB
Vulns
0
Weekly
0
Stars
3.3K

goober

goober, a less than 1KB css-in-js solution.

Backers on Open Collective Sponsors on Open Collective

version status gzip size downloads coverage Slack

The Great Shave Off Challenge

Can you shave off bytes from goober? Do it and you're gonna get paid! More info here

Motivation

I've always wondered if you could get a working solution for css-in-js with a smaller footprint. While I was working on a side project I wanted to use styled-components, or more accurately the styled pattern. Looking at the JavaScript bundle sizes, I quickly realized that I would have to include ~12kB(styled-components) or ~11kB(emotion) just so I can use the styled paradigm. So, I embarked on a mission to create a smaller alternative for these well established APIs.

Why the peanuts emoji?

It's a pun on the tagline.

css-in-js at the cost of peanuts! goober

Talks and Podcasts

Table of contents

Usage

The API is inspired by emotion styled function. Meaning, you call it with your tagName, and it returns a vDOM component for that tag. Note, setup needs to be ran before the styled function is used.

import { h } from 'preact';
import { styled, setup } from 'goober';

// Should be called here, and just once
setup(h);

const Icon = styled('span')`
    display: flex;
    flex: 1;
    color: red;
`;

const Button = styled('button')`
    background: dodgerblue;
    color: white;
    border: ${Math.random()}px solid white;

    &:focus,
    &:hover {
        padding: 1em;
    }

    .otherClass {
        margin: 0;
    }

    ${Icon} {
        color: black;
    }
`;

Examples

Comparison and tradeoffs

In this section I would like to compare goober, as objectively as I can, with the latest versions of two most well known css-in-js packages: styled-components and emotion.

I've used the following markers to reflect the state of each feature:

  • Supported
  • Partially supported
  • Not supported

Here we go:

Feature name Goober Styled Components Emotion
Base bundle size 1.25 kB 12.6 kB 7.4 kB
Framework agnostic *3
Render with target *1
css api
css prop
styled
styled.<tag> *2
default export
as
.withComponent
.attrs
shouldForwardProp
keyframes
Labels
ClassNames
Global styles
SSR
Theming
Tagged Templates
Object styles
Dynamic styles

Footnotes

  • [1] goober can render in any dom target. Meaning you can use goober to define scoped styles in any context. Really useful for web-components.
  • [2] Supported only via babel-plugin-transform-goober
  • [3] Emotion has a framework-agnostic css function. See https://emotion.sh/docs/@emotion/css

SSR

You can get the critical CSS for SSR via extractCss. Take a look at this example: CodeSandbox: SSR with Preact and goober and read the full explanation for extractCSS and targets below.

Benchmarks

The results are included inside the build output as well.

Browser

Coming soon!

SSR

The benchmark is testing the following scenario:

import styled from '<packageName>';

// Create the dynamic styled component
const Foo = styled('div')((props) => ({
    opacity: props.counter > 0.5 ? 1 : 0,
    '@media (min-width: 1px)': {
        rule: 'all'
    },
    '&:hover': {
        another: 1,
        display: 'space'
    }
}));

// Serialize the component
renderToString(<Foo counter={Math.random()} />);

The results are:

goober x 200,437 ops/sec ±1.93% (87 runs sampled)
styled-components@5.2.1 x 12,650 ops/sec ±9.09% (48 runs sampled)
emotion@11.0.0 x 104,229 ops/sec ±2.06% (88 runs sampled)

Fastest is: goober

API

As you can see, goober supports most of the CSS syntax. If you find any issues, please submit a ticket, or open a PR with a fix.

styled(tagName: String | Function, forwardRef?: Function)

  • @param {String|Function} tagName The name of the DOM element you'd like the styles to be applied to
  • @param {Function} forwardRef Forward ref function. Usually React.forwardRef
  • @returns {Function} Returns the tag template function.
import { styled } from 'goober';

const Btn = styled('button')`
    border-radius: 4px;
`;

Different ways of customizing the styles

Tagged templates functions
import { styled } from 'goober';

const Btn = styled('button')`
    border-radius: ${(props) => props.size}px;
`;

<Btn size={20} />;
Function that returns a string
import { styled } from 'goober';

const Btn = styled('button')(
    (props) => `
  border-radius: ${props.size}px;
`
);

<Btn size={20} />;
JSON/Object
import { styled } from 'goober';

const Btn = styled('button')((props) => ({
    borderRadius: props.size + 'px'
}));

<Btn size={20} />;
Arrays
import { styled } from 'goober';

const Btn = styled('button')([
    { color: 'tomato' },
    ({ isPrimary }) => ({ background: isPrimary ? 'cyan' : 'gray' })
]);

<Btn />; // This will render the `Button` with `background: gray;`
<Btn isPrimary />; // This will render the `Button` with `background: cyan;`
Forward ref function

As goober is JSX library agnostic, you need to pass in the forward ref function for the library you are using. Here's how you do it for React.

const Title = styled('h1', React.forwardRef)`
    font-weight: bold;
    color: dodgerblue;
`;
setup(pragma: Function, prefixer?: Function, theme?: Function, forwardProps?: Function)

The call to setup() should occur only once. It should be called in the entry file of your project.

Given the fact that react uses createElement for the transformed elements and preact uses h, setup should be called with the proper pragma function. This was added to reduce the bundled size and being able to bundle an esmodule version. At the moment, it's the best tradeoff I can think of.

import React from 'react';
import { setup } from 'goober';

setup(React.createElement);
With prefixer
import React from 'react';
import { setup } from 'goober';

const customPrefixer = (key, value) => `${key}: ${value};\n`;

setup(React.createElement, customPrefixer);
With theme
import React, { createContext, useContext, createElement } from 'react';
import { setup, styled } from 'goober';

const theme = { primary: 'blue' };
const ThemeContext = createContext(theme);
const useTheme = () => useContext(ThemeContext);

setup(createElement, undefined, useTheme);

const ContainerWithTheme = styled('div')`
    color: ${(props) => props.theme.primary};
`;
With forwardProps

The forwardProps function offers a way to achieve the same shouldForwardProps functionality as emotion and styled-components (with transient props) offer. The difference here is that the function receives the whole props and you are in charge of removing the props that should not end up in the DOM.

This is a super useful functionality when paired with theme object, variants, or any other customisation one might need.

import React from 'react';
import { setup, styled } from 'goober';

setup(React.createElement, undefined, undefined, (props) => {
    for (let prop in props) {
        // Or any other conditions.
        // This could also check if this is a dev build and not remove the props
        if (prop === 'size') {
            delete props[prop];
        }
    }
});

The functionality of "transient props" (with a "$" prefix) can be implemented as follows:

import React from 'react';
import { setup, styled } from 'goober';

setup(React.createElement, undefined, undefined, (props) => {
    for (let prop in props) {
        if (prop[0] === '

Alternatively you can use goober/should-forward-prop addon to pass only the filter function and not have to deal with the full props object.

import React from 'react';
import { setup, styled } from 'goober';
import { shouldForwardProp } from 'goober/should-forward-prop';

setup(
    React.createElement,
    undefined,
    undefined,
    // This package accepts a `filter` function. If you return false that prop
    // won't be included in the forwarded props.
    shouldForwardProp((prop) => {
        return prop !== 'size';
    })
);
css(taggedTemplate)
  • @returns {String} Returns the className.

To create a className, you need to call css with your style rules in a tagged template.

import { css } from "goober";

const BtnClassName = css`
  border-radius: 4px;
`;

// vanilla JS
const btn = document.querySelector("#btn");
// BtnClassName === 'g016232'
btn.classList.add(BtnClassName);

// JSX
// BtnClassName === 'g016232'
const App => <button className={BtnClassName}>click</button>
Different ways of customizing css
Passing props to css tagged templates
import { css } from 'goober';

// JSX
const CustomButton = (props) => (
    <button
        className={css`
            border-radius: ${props.size}px;
        `}
    >
        click
    </button>
);
Using css with JSON/Object
import { css } from 'goober';
const BtnClassName = (props) =>
    css({
        background: props.color,
        borderRadius: props.radius + 'px'
    });

Notice: using css with object can reduce your bundle size.

We can also declare styles at the top of the file by wrapping css into a function that we call to get the className.

import { css } from 'goober';

const BtnClassName = (props) => css`
    border-radius: ${props.size}px;
`;

// vanilla JS
// BtnClassName({size:20}) -> g016360
const btn = document.querySelector('#btn');
btn.classList.add(BtnClassName({ size: 20 }));

// JSX
// BtnClassName({size:20}) -> g016360
const App = () => <button className={BtnClassName({ size: 20 })}>click</button>;

The difference between calling css directly and wrapping into a function is the timing of its execution. The former is when the component(file) is imported, the latter is when it is actually rendered.

If you use extractCSS for SSR, you may prefer to use the latter, or the styled API to avoid inconsistent results.

targets

By default, goober will append a style tag to the <head> of a document. You might want to target a different node, for instance, when you want to use goober with web components (so you'd want it to append style tags to individual shadowRoots). For this purpose, you can .bind a new target to the styled and css methods:

import * as goober from 'goober';
const target = document.getElementById('target');
const css = goober.css.bind({ target: target });
const styled = goober.styled.bind({ target: target });

If you don't provide a target, goober always defaults to <head> and in environments without a DOM (think certain SSR solutions), it will just use a plain string cache to store generated styles which you can extract with extractCSS(see below).

extractCss(target?)
  • @returns {String}

Returns the <style> tag that is rendered in a target and clears the style sheet. Defaults to <head>.

const { extractCss } = require('goober');

// After your app has rendered, just call it:
const styleTag = `<style id="_goober">${extractCss()}</style>`;

// Note: To be able to `hydrate` the styles you should use the proper `id` so `goober` can pick it up and use it as the target from now on
createGlobalStyles

To define your global styles you need to create a GlobalStyles component and use it as part of your tree. The createGlobalStyles is available at goober/global addon.

import { createGlobalStyles } from 'goober/global';

const GlobalStyles = createGlobalStyles`
  html, 
  body {
    background: light;
  }

  * {
    box-sizing: border-box;
  }
`;

export default function App() {
    return (
        <div id="root">
            <GlobalStyles />
            <Navigation>
            <RestOfYourApp>
        </div>
    )
}
How about using glob function directly?

Before the global addon, goober/global, there was a method named glob that was part of the main package that would do the same thing, more or less. Having only that method to define global styles usually led to missing global styles from the extracted css, since the pattern did not enforce the evaluation of the styles at render time. The glob method is still exported from goober/global, in case you have a hard dependency on it. It still has the same API:

import { glob } from 'goober';

glob`
  html, 
  body {
    background: light;
  }

  * {
    box-sizing: border-box;
  }
`;
keyframes

keyframes is a helpful method to define reusable animations that can be decoupled from the main style declaration and shared across components.

import { keyframes } from 'goober';

const rotate = keyframes`
    from, to {
        transform: rotate(0deg);
    }

    50% {
        transform: rotate(180deg);
    }
`;

const Wicked = styled('div')`
    background: tomato;
    color: white;
    animation: ${rotate} 1s ease-in-out;
`;
shouldForwardProp

To implement the shouldForwardProp without the need to provide the full loop over props you can use the goober/should-forward-prop addon.

import { h } from 'preact';
import { setup } from 'goober';
import { shouldForwardProp } from 'goober/should-forward-prop';

setup(
    h,
    undefined,
    undefined,
    shouldForwardProp((prop) => {
        // Do NOT forward props that start with `

goober

goober, a less than 1KB css-in-js solution.

Backers on Open Collective Sponsors on Open Collective

version status gzip size downloads coverage Slack

The Great Shave Off Challenge

Can you shave off bytes from goober? Do it and you're gonna get paid! More info here

Motivation

I've always wondered if you could get a working solution for css-in-js with a smaller footprint. While I was working on a side project I wanted to use styled-components, or more accurately the __INLINE_CODE_0__ pattern. Looking at the JavaScript bundle sizes, I quickly realized that I would have to include ~12kB(styled-components) or ~11kB(emotion) just so I can use the __INLINE_CODE_1__ paradigm. So, I embarked on a mission to create a smaller alternative for these well established APIs.

Why the peanuts emoji?

It's a pun on the tagline.

css-in-js at the cost of peanuts! goober

Talks and Podcasts

Table of contents

Usage

The API is inspired by emotion __INLINE_CODE_2__ function. Meaning, you call it with your __INLINE_CODE_3__, and it returns a vDOM component for that tag. Note, __INLINE_CODE_4__ needs to be ran before the __INLINE_CODE_5__ function is used.

import { h } from 'preact';
import { styled, setup } from 'goober';

// Should be called here, and just once
setup(h);

const Icon = styled('span')`
    display: flex;
    flex: 1;
    color: red;
`;

const Button = styled('button')`
    background: dodgerblue;
    color: white;
    border: ${Math.random()}px solid white;

    &:focus,
    &:hover {
        padding: 1em;
    }

    .otherClass {
        margin: 0;
    }

    ${Icon} {
        color: black;
    }
`;

Examples

Comparison and tradeoffs

In this section I would like to compare goober, as objectively as I can, with the latest versions of two most well known css-in-js packages: styled-components and emotion.

I've used the following markers to reflect the state of each feature:

  • Supported
  • Partially supported
  • Not supported

Here we go:

Feature name Goober Styled Components Emotion
Base bundle size 1.25 kB 12.6 kB 7.4 kB
Framework agnostic *3
Render with target *1
__INLINE_CODE_6__ api
__INLINE_CODE_7__ prop
__INLINE_CODE_8__
__INLINE_CODE_9__ *2
default export
__INLINE_CODE_10__
__INLINE_CODE_11__
__INLINE_CODE_12__
__INLINE_CODE_13__
__INLINE_CODE_14__
Labels
ClassNames
Global styles
SSR
Theming
Tagged Templates
Object styles
Dynamic styles

Footnotes

  • [1] __INLINE_CODE_15__ can render in any dom target. Meaning you can use __INLINE_CODE_16__ to define scoped styles in any context. Really useful for web-components.
  • [2] Supported only via __INLINE_CODE_17__
  • [3] Emotion has a framework-agnostic __INLINE_CODE_18__ function. See https://emotion.sh/docs/@emotion/css

SSR

You can get the critical CSS for SSR via __INLINE_CODE_19__. Take a look at this example: CodeSandbox: SSR with Preact and goober and read the full explanation for __INLINE_CODE_20__ and __INLINE_CODE_21__ below.

Benchmarks

The results are included inside the build output as well.

Browser

Coming soon!

SSR

The benchmark is testing the following scenario:

import styled from '<packageName>';

// Create the dynamic styled component
const Foo = styled('div')((props) => ({
    opacity: props.counter > 0.5 ? 1 : 0,
    '@media (min-width: 1px)': {
        rule: 'all'
    },
    '&:hover': {
        another: 1,
        display: 'space'
    }
}));

// Serialize the component
renderToString(<Foo counter={Math.random()} />);

The results are:

goober x 200,437 ops/sec ±1.93% (87 runs sampled)
styled-components@5.2.1 x 12,650 ops/sec ±9.09% (48 runs sampled)
emotion@11.0.0 x 104,229 ops/sec ±2.06% (88 runs sampled)

Fastest is: goober

API

As you can see, goober supports most of the CSS syntax. If you find any issues, please submit a ticket, or open a PR with a fix.

__INLINE_CODE_22__

  • __INLINE_CODE_23__ The name of the DOM element you'd like the styles to be applied to
  • __INLINE_CODE_24__ Forward ref function. Usually __INLINE_CODE_25__
  • __INLINE_CODE_26__ Returns the tag template function.
import { styled } from 'goober';

const Btn = styled('button')`
    border-radius: 4px;
`;

Different ways of customizing the styles

Tagged templates functions
import { styled } from 'goober';

const Btn = styled('button')`
    border-radius: ${(props) => props.size}px;
`;

<Btn size={20} />;
Function that returns a string
import { styled } from 'goober';

const Btn = styled('button')(
    (props) => `
  border-radius: ${props.size}px;
`
);

<Btn size={20} />;
JSON/Object
import { styled } from 'goober';

const Btn = styled('button')((props) => ({
    borderRadius: props.size + 'px'
}));

<Btn size={20} />;
Arrays
import { styled } from 'goober';

const Btn = styled('button')([
    { color: 'tomato' },
    ({ isPrimary }) => ({ background: isPrimary ? 'cyan' : 'gray' })
]);

<Btn />; // This will render the `Button` with `background: gray;`
<Btn isPrimary />; // This will render the `Button` with `background: cyan;`
Forward ref function

As goober is JSX library agnostic, you need to pass in the forward ref function for the library you are using. Here's how you do it for React.

const Title = styled('h1', React.forwardRef)`
    font-weight: bold;
    color: dodgerblue;
`;
__INLINE_CODE_27__

The call to __INLINE_CODE_28__ should occur only once. It should be called in the entry file of your project.

Given the fact that __INLINE_CODE_29__ uses __INLINE_CODE_30__ for the transformed elements and __INLINE_CODE_31__ uses __INLINE_CODE_32__, __INLINE_CODE_33__ should be called with the proper pragma function. This was added to reduce the bundled size and being able to bundle an esmodule version. At the moment, it's the best tradeoff I can think of.

import React from 'react';
import { setup } from 'goober';

setup(React.createElement);
With prefixer
import React from 'react';
import { setup } from 'goober';

const customPrefixer = (key, value) => `${key}: ${value};\n`;

setup(React.createElement, customPrefixer);
With theme
import React, { createContext, useContext, createElement } from 'react';
import { setup, styled } from 'goober';

const theme = { primary: 'blue' };
const ThemeContext = createContext(theme);
const useTheme = () => useContext(ThemeContext);

setup(createElement, undefined, useTheme);

const ContainerWithTheme = styled('div')`
    color: ${(props) => props.theme.primary};
`;
With forwardProps

The __INLINE_CODE_34__ function offers a way to achieve the same __INLINE_CODE_35__ functionality as emotion and styled-components (with transient props) offer. The difference here is that the function receives the whole props and you are in charge of removing the props that should not end up in the DOM.

This is a super useful functionality when paired with theme object, variants, or any other customisation one might need.

import React from 'react';
import { setup, styled } from 'goober';

setup(React.createElement, undefined, undefined, (props) => {
    for (let prop in props) {
        // Or any other conditions.
        // This could also check if this is a dev build and not remove the props
        if (prop === 'size') {
            delete props[prop];
        }
    }
});

The functionality of "transient props" (with a "$" prefix) can be implemented as follows:

import React from 'react';
import { setup, styled } from 'goober';

setup(React.createElement, undefined, undefined, (props) => {
    for (let prop in props) {
        if (prop[0] === '

Alternatively you can use __INLINE_CODE_36__ addon to pass only the filter function and not have to deal with the full __INLINE_CODE_37__ object.

import React from 'react';
import { setup, styled } from 'goober';
import { shouldForwardProp } from 'goober/should-forward-prop';

setup(
    React.createElement,
    undefined,
    undefined,
    // This package accepts a `filter` function. If you return false that prop
    // won't be included in the forwarded props.
    shouldForwardProp((prop) => {
        return prop !== 'size';
    })
);
__INLINE_CODE_38__
  • __INLINE_CODE_39__ Returns the className.

To create a className, you need to call __INLINE_CODE_40__ with your style rules in a tagged template.

import { css } from "goober";

const BtnClassName = css`
  border-radius: 4px;
`;

// vanilla JS
const btn = document.querySelector("#btn");
// BtnClassName === 'g016232'
btn.classList.add(BtnClassName);

// JSX
// BtnClassName === 'g016232'
const App => <button className={BtnClassName}>click</button>
Different ways of customizing __INLINE_CODE_41__
Passing props to __INLINE_CODE_42__ tagged templates
import { css } from 'goober';

// JSX
const CustomButton = (props) => (
    <button
        className={css`
            border-radius: ${props.size}px;
        `}
    >
        click
    </button>
);
Using __INLINE_CODE_43__ with JSON/Object
import { css } from 'goober';
const BtnClassName = (props) =>
    css({
        background: props.color,
        borderRadius: props.radius + 'px'
    });

Notice: using __INLINE_CODE_44__ with object can reduce your bundle size.

We can also declare styles at the top of the file by wrapping __INLINE_CODE_45__ into a function that we call to get the className.

import { css } from 'goober';

const BtnClassName = (props) => css`
    border-radius: ${props.size}px;
`;

// vanilla JS
// BtnClassName({size:20}) -> g016360
const btn = document.querySelector('#btn');
btn.classList.add(BtnClassName({ size: 20 }));

// JSX
// BtnClassName({size:20}) -> g016360
const App = () => <button className={BtnClassName({ size: 20 })}>click</button>;

The difference between calling __INLINE_CODE_46__ directly and wrapping into a function is the timing of its execution. The former is when the component(file) is imported, the latter is when it is actually rendered.

If you use __INLINE_CODE_47__ for SSR, you may prefer to use the latter, or the __INLINE_CODE_48__ API to avoid inconsistent results.

__INLINE_CODE_49__

By default, goober will append a style tag to the __INLINE_CODE_50__ of a document. You might want to target a different node, for instance, when you want to use goober with web components (so you'd want it to append style tags to individual shadowRoots). For this purpose, you can __INLINE_CODE_51__ a new target to the __INLINE_CODE_52__ and __INLINE_CODE_53__ methods:

import * as goober from 'goober';
const target = document.getElementById('target');
const css = goober.css.bind({ target: target });
const styled = goober.styled.bind({ target: target });

If you don't provide a target, goober always defaults to __INLINE_CODE_54__ and in environments without a DOM (think certain SSR solutions), it will just use a plain string cache to store generated styles which you can extract with __INLINE_CODE_55__(see below).

__INLINE_CODE_56__
  • __INLINE_CODE_57__

Returns the __INLINE_CODE_58__ tag that is rendered in a target and clears the style sheet. Defaults to __INLINE_CODE_59__.

const { extractCss } = require('goober');

// After your app has rendered, just call it:
const styleTag = `<style id="_goober">${extractCss()}</style>`;

// Note: To be able to `hydrate` the styles you should use the proper `id` so `goober` can pick it up and use it as the target from now on
__INLINE_CODE_60__

To define your global styles you need to create a __INLINE_CODE_61__ component and use it as part of your tree. The __INLINE_CODE_62__ is available at __INLINE_CODE_63__ addon.

import { createGlobalStyles } from 'goober/global';

const GlobalStyles = createGlobalStyles`
  html, 
  body {
    background: light;
  }

  * {
    box-sizing: border-box;
  }
`;

export default function App() {
    return (
        <div id="root">
            <GlobalStyles />
            <Navigation>
            <RestOfYourApp>
        </div>
    )
}
How about using __INLINE_CODE_64__ function directly?

Before the global addon, __INLINE_CODE_65__, there was a method named __INLINE_CODE_66__ that was part of the main package that would do the same thing, more or less. Having only that method to define global styles usually led to missing global styles from the extracted css, since the pattern did not enforce the evaluation of the styles at render time. The __INLINE_CODE_67__ method is still exported from __INLINE_CODE_68__, in case you have a hard dependency on it. It still has the same API:

import { glob } from 'goober';

glob`
  html, 
  body {
    background: light;
  }

  * {
    box-sizing: border-box;
  }
`;
__INLINE_CODE_69__

__INLINE_CODE_70__ is a helpful method to define reusable animations that can be decoupled from the main style declaration and shared across components.

import { keyframes } from 'goober';

const rotate = keyframes`
    from, to {
        transform: rotate(0deg);
    }

    50% {
        transform: rotate(180deg);
    }
`;

const Wicked = styled('div')`
    background: tomato;
    color: white;
    animation: ${rotate} 1s ease-in-out;
`;
__INLINE_CODE_71__

To implement the __INLINE_CODE_72__ without the need to provide the full loop over __INLINE_CODE_73__ you can use the __INLINE_CODE_74__ addon.

symbol
return prop['0'] !== '

Integrations

Babel plugin

You're in love with the styled.div syntax? Fear no more! We got you covered with a babel plugin that will take your lovely syntax from styled.tag and translate it to goober's styled("tag") call.

npm i --save-dev babel-plugin-transform-goober
# or
yarn add --dev babel-plugin-transform-goober

Visit the package in here for more info (https://github.com/cristianbote/goober/tree/master/packages/babel-plugin-transform-goober)

Babel macro plugin

A babel-plugin-macros macro for [goober][goober], rewriting styled.div syntax to styled('div') calls.

Usage

Once you've configured babel-plugin-macros, change your imports from goober to goober/macro.

Now you can create your components using styled.* syntax:.

import { styled } from 'goober/macro';

const Button = styled.button`
    margin: 0;
    padding: 1rem;
    font-size: 1rem;
    background-color: tomato;
`;

Next.js

Want to use goober with Next.js? We've got you covered! Follow the example below or from the main examples directory.

npx create-next-app --example with-goober with-goober-app
# or
yarn create next-app --example with-goober with-goober-app

Gatsby

Want to use goober with Gatsby? We've got you covered! We have our own plugin to deal with styling your Gatsby projects.

npm i --save goober gatsby-plugin-goober
# or
yarn add goober gatsby-plugin-goober

Preact CLI plugin

If you use Goober with Preact CLI, you can use preact-cli-goober-ssr

npm i --save-dev preact-cli-goober-ssr
# or
yarn add --dev preact-cli-goober-ssr

# preact.config.js
const gooberPlugin = require('preact-cli-goober-ssr')

export default (config, env) => {
  gooberPlugin(config, env)
}

When you build your Preact application, this will run extractCss on your pre-rendered pages and add critical styles for each page.

CSS Prop

You can use a custom css prop to pass in styles on HTML elements with this Babel plugin.

Installation:

npm install --save-dev @agney/babel-plugin-goober-css-prop

List the plugin in .babelrc:

{
  "plugins": [
    "@agney/babel-plugin-goober-css-prop"
  ]
}

Usage:

<main
    css={`
        display: flex;
        min-height: 100vh;
        justify-content: center;
        align-items: center;
    `}
>
    <h1 css="color: dodgerblue">Goober</h1>
</main>

Features

  • Basic CSS parsing
  • Nested rules with pseudo selectors
  • Nested styled components
  • Extending Styles
  • Media queries (@media)
  • Keyframes (@keyframes)
  • Smart (lazy) client-side hydration
  • Styling any component
    • via const Btn = ({className}) => {...}; const TomatoBtn = styled(Btn)`color: tomato;`
  • Vanilla (via css function)
  • globalStyle (via glob) so one would be able to create global styles
  • target/extract from elements other than <head>
  • vendor prefixing

Content Security Policy (CSP)

goober supports Content Security Policy nonces for inline styles. Set window.__nonce__ before loading the library:

<script nonce="your-nonce-here">
  window.__nonce__ = 'your-nonce-here';
</script>

The nonce will be added to goober's <style> element.

Sharing style

There are a couple of ways to effectively share/extend styles across components.

Extending

You can extend the desired component that needs to be enriched or overwritten with another set of css rules.

import { styled } from 'goober';

// Let's declare a primitive for our styled component
const Primitive = styled('span')`
    margin: 0;
    padding: 0;
`;

// Later on we could get the primitive shared styles and also add our owns
const Container = styled(Primitive)`
    padding: 1em;
`;

Using as prop

Another helpful way to extend a certain component is with the as property. Given our example above we could modify it like:

import { styled } from 'goober';

// Our primitive element
const Primitive = styled('span')`
    margin: 0;
    padding: 0;
`;

const Container = styled('div')`
    padding: 1em;
`;

// At composition/render time
<Primitive as={'div'} /> // <div class="go01234" />

// Or using the `Container`
<Primitive as={Container} /> // <div class="go01234 go56789" />

Autoprefixer

Autoprefixing is a helpful way to make sure the generated css will work seamlessly on the whole spectrum of browsers. With that in mind, the core goober package can't hold that logic to determine the autoprefixing needs, so we added a new package that you can choose to address them.

npm install goober
# or
yarn add goober

After the main package is installed it's time to bootstrap goober with it:

import { setup } from 'goober';
import { prefix } from 'goober/prefixer';

// Bootstrap goober
setup(React.createElement, prefix);

And voilà! It is done!

TypeScript

goober comes with type definitions build in, making it easy to get started in TypeScript straight away.

Prop Types

If you're using custom props and wish to style based on them, you can do so as follows:

interface Props {
    size: number;
}

styled('div')<Props>`
    border-radius: ${(props) => props.size}px;
`;

// This also works!

styled<Props>('div')`
    border-radius: ${(props) => props.size}px;
`;

Extending Theme

If you're using a custom theme and want to add types to it, you can create a declaration file at the base of your project.

// goober.d.t.s

import 'goober';

declare module 'goober' {
    export interface DefaultTheme {
        colors: {
            primary: string;
        };
    }
}

You should now have autocompletion for your theme.

const ThemeContainer = styled('div')`
    background-color: ${(props) => props.theme.colors.primary};
`;

Browser support

goober supports all major browsers (Chrome, Edge, Firefox, Safari).

To support IE 11 and older browsers, make sure to use a tool like Babel to transform your code into code that works in the browsers you target.

Contributing

Feel free to try it out and checkout the examples. If you wanna fix something feel free to open a issue or a PR.

Backers

Thank you to all our backers!

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website.

) { delete props[prop]; } } });

Alternatively you can use __INLINE_CODE_36__ addon to pass only the filter function and not have to deal with the full __INLINE_CODE_37__ object.

__CODE_BLOCK_14__
__INLINE_CODE_38__
  • __INLINE_CODE_39__ Returns the className.

To create a className, you need to call __INLINE_CODE_40__ with your style rules in a tagged template.

__CODE_BLOCK_15__
Different ways of customizing __INLINE_CODE_41__
Passing props to __INLINE_CODE_42__ tagged templates
__CODE_BLOCK_16__
Using __INLINE_CODE_43__ with JSON/Object
__CODE_BLOCK_17__

Notice: using __INLINE_CODE_44__ with object can reduce your bundle size.

We can also declare styles at the top of the file by wrapping __INLINE_CODE_45__ into a function that we call to get the className.

__CODE_BLOCK_18__

The difference between calling __INLINE_CODE_46__ directly and wrapping into a function is the timing of its execution. The former is when the component(file) is imported, the latter is when it is actually rendered.

If you use __INLINE_CODE_47__ for SSR, you may prefer to use the latter, or the __INLINE_CODE_48__ API to avoid inconsistent results.

__INLINE_CODE_49__

By default, goober will append a style tag to the __INLINE_CODE_50__ of a document. You might want to target a different node, for instance, when you want to use goober with web components (so you'd want it to append style tags to individual shadowRoots). For this purpose, you can __INLINE_CODE_51__ a new target to the __INLINE_CODE_52__ and __INLINE_CODE_53__ methods:

__CODE_BLOCK_19__

If you don't provide a target, goober always defaults to __INLINE_CODE_54__ and in environments without a DOM (think certain SSR solutions), it will just use a plain string cache to store generated styles which you can extract with __INLINE_CODE_55__(see below).

__INLINE_CODE_56__
  • __INLINE_CODE_57__

Returns the __INLINE_CODE_58__ tag that is rendered in a target and clears the style sheet. Defaults to __INLINE_CODE_59__.

__CODE_BLOCK_20__
__INLINE_CODE_60__

To define your global styles you need to create a __INLINE_CODE_61__ component and use it as part of your tree. The __INLINE_CODE_62__ is available at __INLINE_CODE_63__ addon.

__CODE_BLOCK_21__
How about using __INLINE_CODE_64__ function directly?

Before the global addon, __INLINE_CODE_65__, there was a method named __INLINE_CODE_66__ that was part of the main package that would do the same thing, more or less. Having only that method to define global styles usually led to missing global styles from the extracted css, since the pattern did not enforce the evaluation of the styles at render time. The __INLINE_CODE_67__ method is still exported from __INLINE_CODE_68__, in case you have a hard dependency on it. It still has the same API:

__CODE_BLOCK_22__
__INLINE_CODE_69__

__INLINE_CODE_70__ is a helpful method to define reusable animations that can be decoupled from the main style declaration and shared across components.

__CODE_BLOCK_23__
__INLINE_CODE_71__

To implement the __INLINE_CODE_72__ without the need to provide the full loop over __INLINE_CODE_73__ you can use the __INLINE_CODE_74__ addon.

__CODE_BLOCK_24__

Integrations

Babel plugin

You're in love with the __INLINE_CODE_75__ syntax? Fear no more! We got you covered with a babel plugin that will take your lovely syntax from __INLINE_CODE_76__ and translate it to goober's __INLINE_CODE_77__ call.

__CODE_BLOCK_25__

Visit the package in here for more info (https://github.com/cristianbote/goober/tree/master/packages/babel-plugin-transform-goober)

Babel macro plugin

A babel-plugin-macros macro for [goober][goober], rewriting __INLINE_CODE_78__ syntax to __INLINE_CODE_79__ calls.

Usage

Once you've configured babel-plugin-macros, change your imports from __INLINE_CODE_80__ to __INLINE_CODE_81__.

Now you can create your components using __INLINE_CODE_82__ syntax:.

__CODE_BLOCK_26__

Next.js

Want to use __INLINE_CODE_83__ with Next.js? We've got you covered! Follow the example below or from the main examples directory.

__CODE_BLOCK_27__

Gatsby

Want to use __INLINE_CODE_84__ with Gatsby? We've got you covered! We have our own plugin to deal with styling your Gatsby projects.

__CODE_BLOCK_28__

Preact CLI plugin

If you use Goober with Preact CLI, you can use preact-cli-goober-ssr

__CODE_BLOCK_29__

When you build your Preact application, this will run __INLINE_CODE_85__ on your pre-rendered pages and add critical styles for each page.

CSS Prop

You can use a custom __INLINE_CODE_86__ prop to pass in styles on HTML elements with this Babel plugin.

Installation:

__CODE_BLOCK_30__

List the plugin in __INLINE_CODE_87__:

__CODE_BLOCK_31__

Usage:

__CODE_BLOCK_32__

Features

  • Basic CSS parsing
  • Nested rules with pseudo selectors
  • Nested styled components
  • Extending Styles
  • Media queries (@media)
  • Keyframes (@keyframes)
  • Smart (lazy) client-side hydration
  • Styling any component
    • via __INLINE_CODE_88__
  • Vanilla (via __INLINE_CODE_89__ function)
  • __INLINE_CODE_90__ (via __INLINE_CODE_91__) so one would be able to create global styles
  • target/extract from elements other than __INLINE_CODE_92__
  • vendor prefixing

Content Security Policy (CSP)

goober supports Content Security Policy nonces for inline styles. Set __INLINE_CODE_93__ before loading the library:

__CODE_BLOCK_33__

The nonce will be added to goober's __INLINE_CODE_94__ element.

Sharing style

There are a couple of ways to effectively share/extend styles across components.

Extending

You can extend the desired component that needs to be enriched or overwritten with another set of css rules.

__CODE_BLOCK_34__

Using __INLINE_CODE_95__ prop

Another helpful way to extend a certain component is with the __INLINE_CODE_96__ property. Given our example above we could modify it like:

__CODE_BLOCK_35__

Autoprefixer

Autoprefixing is a helpful way to make sure the generated css will work seamlessly on the whole spectrum of browsers. With that in mind, the core __INLINE_CODE_97__ package can't hold that logic to determine the autoprefixing needs, so we added a new package that you can choose to address them.

__CODE_BLOCK_36__

After the main package is installed it's time to bootstrap goober with it:

__CODE_BLOCK_37__

And voilà! It is done!

TypeScript

__INLINE_CODE_98__ comes with type definitions build in, making it easy to get started in TypeScript straight away.

Prop Types

If you're using custom props and wish to style based on them, you can do so as follows:

__CODE_BLOCK_38__

Extending Theme

If you're using a custom theme and want to add types to it, you can create a declaration file at the base of your project.

__CODE_BLOCK_39__

You should now have autocompletion for your theme.

__CODE_BLOCK_40__

Browser support

__INLINE_CODE_99__ supports all major browsers (Chrome, Edge, Firefox, Safari).

To support IE 11 and older browsers, make sure to use a tool like Babel to transform your code into code that works in the browsers you target.

Contributing

Feel free to try it out and checkout the examples. If you wanna fix something feel free to open a issue or a PR.

Backers

Thank you to all our backers!

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website.

; }) );

Integrations

Babel plugin

You're in love with the __INLINE_CODE_75__ syntax? Fear no more! We got you covered with a babel plugin that will take your lovely syntax from __INLINE_CODE_76__ and translate it to goober's __INLINE_CODE_77__ call.

__CODE_BLOCK_25__

Visit the package in here for more info (https://github.com/cristianbote/goober/tree/master/packages/babel-plugin-transform-goober)

Babel macro plugin

A babel-plugin-macros macro for [goober][goober], rewriting __INLINE_CODE_78__ syntax to __INLINE_CODE_79__ calls.

Usage

Once you've configured babel-plugin-macros, change your imports from __INLINE_CODE_80__ to __INLINE_CODE_81__.

Now you can create your components using __INLINE_CODE_82__ syntax:.

__CODE_BLOCK_26__

Next.js

Want to use __INLINE_CODE_83__ with Next.js? We've got you covered! Follow the example below or from the main examples directory.

__CODE_BLOCK_27__

Gatsby

Want to use __INLINE_CODE_84__ with Gatsby? We've got you covered! We have our own plugin to deal with styling your Gatsby projects.

__CODE_BLOCK_28__

Preact CLI plugin

If you use Goober with Preact CLI, you can use preact-cli-goober-ssr

__CODE_BLOCK_29__

When you build your Preact application, this will run __INLINE_CODE_85__ on your pre-rendered pages and add critical styles for each page.

CSS Prop

You can use a custom __INLINE_CODE_86__ prop to pass in styles on HTML elements with this Babel plugin.

Installation:

__CODE_BLOCK_30__

List the plugin in __INLINE_CODE_87__:

__CODE_BLOCK_31__

Usage:

__CODE_BLOCK_32__

Features

  • Basic CSS parsing
  • Nested rules with pseudo selectors
  • Nested styled components
  • Extending Styles
  • Media queries (@media)
  • Keyframes (@keyframes)
  • Smart (lazy) client-side hydration
  • Styling any component
    • via __INLINE_CODE_88__
  • Vanilla (via __INLINE_CODE_89__ function)
  • __INLINE_CODE_90__ (via __INLINE_CODE_91__) so one would be able to create global styles
  • target/extract from elements other than __INLINE_CODE_92__
  • vendor prefixing

Content Security Policy (CSP)

goober supports Content Security Policy nonces for inline styles. Set __INLINE_CODE_93__ before loading the library:

__CODE_BLOCK_33__

The nonce will be added to goober's __INLINE_CODE_94__ element.

Sharing style

There are a couple of ways to effectively share/extend styles across components.

Extending

You can extend the desired component that needs to be enriched or overwritten with another set of css rules.

__CODE_BLOCK_34__

Using __INLINE_CODE_95__ prop

Another helpful way to extend a certain component is with the __INLINE_CODE_96__ property. Given our example above we could modify it like:

__CODE_BLOCK_35__

Autoprefixer

Autoprefixing is a helpful way to make sure the generated css will work seamlessly on the whole spectrum of browsers. With that in mind, the core __INLINE_CODE_97__ package can't hold that logic to determine the autoprefixing needs, so we added a new package that you can choose to address them.

__CODE_BLOCK_36__

After the main package is installed it's time to bootstrap goober with it:

__CODE_BLOCK_37__

And voilà! It is done!

TypeScript

__INLINE_CODE_98__ comes with type definitions build in, making it easy to get started in TypeScript straight away.

Prop Types

If you're using custom props and wish to style based on them, you can do so as follows:

__CODE_BLOCK_38__

Extending Theme

If you're using a custom theme and want to add types to it, you can create a declaration file at the base of your project.

__CODE_BLOCK_39__

You should now have autocompletion for your theme.

__CODE_BLOCK_40__

Browser support

__INLINE_CODE_99__ supports all major browsers (Chrome, Edge, Firefox, Safari).

To support IE 11 and older browsers, make sure to use a tool like Babel to transform your code into code that works in the browsers you target.

Contributing

Feel free to try it out and checkout the examples. If you wanna fix something feel free to open a issue or a PR.

Backers

Thank you to all our backers!

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website.

) { delete props[prop]; } } });

Alternatively you can use __INLINE_CODE_36__ addon to pass only the filter function and not have to deal with the full __INLINE_CODE_37__ object.

__CODE_BLOCK_14__
__INLINE_CODE_38__
  • __INLINE_CODE_39__ Returns the className.

To create a className, you need to call __INLINE_CODE_40__ with your style rules in a tagged template.

__CODE_BLOCK_15__
Different ways of customizing __INLINE_CODE_41__
Passing props to __INLINE_CODE_42__ tagged templates
__CODE_BLOCK_16__
Using __INLINE_CODE_43__ with JSON/Object
__CODE_BLOCK_17__

Notice: using __INLINE_CODE_44__ with object can reduce your bundle size.

We can also declare styles at the top of the file by wrapping __INLINE_CODE_45__ into a function that we call to get the className.

__CODE_BLOCK_18__

The difference between calling __INLINE_CODE_46__ directly and wrapping into a function is the timing of its execution. The former is when the component(file) is imported, the latter is when it is actually rendered.

If you use __INLINE_CODE_47__ for SSR, you may prefer to use the latter, or the __INLINE_CODE_48__ API to avoid inconsistent results.

__INLINE_CODE_49__

By default, goober will append a style tag to the __INLINE_CODE_50__ of a document. You might want to target a different node, for instance, when you want to use goober with web components (so you'd want it to append style tags to individual shadowRoots). For this purpose, you can __INLINE_CODE_51__ a new target to the __INLINE_CODE_52__ and __INLINE_CODE_53__ methods:

__CODE_BLOCK_19__

If you don't provide a target, goober always defaults to __INLINE_CODE_54__ and in environments without a DOM (think certain SSR solutions), it will just use a plain string cache to store generated styles which you can extract with __INLINE_CODE_55__(see below).

__INLINE_CODE_56__
  • __INLINE_CODE_57__

Returns the __INLINE_CODE_58__ tag that is rendered in a target and clears the style sheet. Defaults to __INLINE_CODE_59__.

__CODE_BLOCK_20__
__INLINE_CODE_60__

To define your global styles you need to create a __INLINE_CODE_61__ component and use it as part of your tree. The __INLINE_CODE_62__ is available at __INLINE_CODE_63__ addon.

__CODE_BLOCK_21__
How about using __INLINE_CODE_64__ function directly?

Before the global addon, __INLINE_CODE_65__, there was a method named __INLINE_CODE_66__ that was part of the main package that would do the same thing, more or less. Having only that method to define global styles usually led to missing global styles from the extracted css, since the pattern did not enforce the evaluation of the styles at render time. The __INLINE_CODE_67__ method is still exported from __INLINE_CODE_68__, in case you have a hard dependency on it. It still has the same API:

__CODE_BLOCK_22__
__INLINE_CODE_69__

__INLINE_CODE_70__ is a helpful method to define reusable animations that can be decoupled from the main style declaration and shared across components.

__CODE_BLOCK_23__
__INLINE_CODE_71__

To implement the __INLINE_CODE_72__ without the need to provide the full loop over __INLINE_CODE_73__ you can use the __INLINE_CODE_74__ addon.

__CODE_BLOCK_24__

Integrations

Babel plugin

You're in love with the __INLINE_CODE_75__ syntax? Fear no more! We got you covered with a babel plugin that will take your lovely syntax from __INLINE_CODE_76__ and translate it to goober's __INLINE_CODE_77__ call.

__CODE_BLOCK_25__

Visit the package in here for more info (https://github.com/cristianbote/goober/tree/master/packages/babel-plugin-transform-goober)

Babel macro plugin

A babel-plugin-macros macro for [goober][goober], rewriting __INLINE_CODE_78__ syntax to __INLINE_CODE_79__ calls.

Usage

Once you've configured babel-plugin-macros, change your imports from __INLINE_CODE_80__ to __INLINE_CODE_81__.

Now you can create your components using __INLINE_CODE_82__ syntax:.

__CODE_BLOCK_26__

Next.js

Want to use __INLINE_CODE_83__ with Next.js? We've got you covered! Follow the example below or from the main examples directory.

__CODE_BLOCK_27__

Gatsby

Want to use __INLINE_CODE_84__ with Gatsby? We've got you covered! We have our own plugin to deal with styling your Gatsby projects.

__CODE_BLOCK_28__

Preact CLI plugin

If you use Goober with Preact CLI, you can use preact-cli-goober-ssr

__CODE_BLOCK_29__

When you build your Preact application, this will run __INLINE_CODE_85__ on your pre-rendered pages and add critical styles for each page.

CSS Prop

You can use a custom __INLINE_CODE_86__ prop to pass in styles on HTML elements with this Babel plugin.

Installation:

__CODE_BLOCK_30__

List the plugin in __INLINE_CODE_87__:

__CODE_BLOCK_31__

Usage:

__CODE_BLOCK_32__

Features

  • Basic CSS parsing
  • Nested rules with pseudo selectors
  • Nested styled components
  • Extending Styles
  • Media queries (@media)
  • Keyframes (@keyframes)
  • Smart (lazy) client-side hydration
  • Styling any component
    • via __INLINE_CODE_88__
  • Vanilla (via __INLINE_CODE_89__ function)
  • __INLINE_CODE_90__ (via __INLINE_CODE_91__) so one would be able to create global styles
  • target/extract from elements other than __INLINE_CODE_92__
  • vendor prefixing

Content Security Policy (CSP)

goober supports Content Security Policy nonces for inline styles. Set __INLINE_CODE_93__ before loading the library:

__CODE_BLOCK_33__

The nonce will be added to goober's __INLINE_CODE_94__ element.

Sharing style

There are a couple of ways to effectively share/extend styles across components.

Extending

You can extend the desired component that needs to be enriched or overwritten with another set of css rules.

__CODE_BLOCK_34__

Using __INLINE_CODE_95__ prop

Another helpful way to extend a certain component is with the __INLINE_CODE_96__ property. Given our example above we could modify it like:

__CODE_BLOCK_35__

Autoprefixer

Autoprefixing is a helpful way to make sure the generated css will work seamlessly on the whole spectrum of browsers. With that in mind, the core __INLINE_CODE_97__ package can't hold that logic to determine the autoprefixing needs, so we added a new package that you can choose to address them.

__CODE_BLOCK_36__

After the main package is installed it's time to bootstrap goober with it:

__CODE_BLOCK_37__

And voilà! It is done!

TypeScript

__INLINE_CODE_98__ comes with type definitions build in, making it easy to get started in TypeScript straight away.

Prop Types

If you're using custom props and wish to style based on them, you can do so as follows:

__CODE_BLOCK_38__

Extending Theme

If you're using a custom theme and want to add types to it, you can create a declaration file at the base of your project.

__CODE_BLOCK_39__

You should now have autocompletion for your theme.

__CODE_BLOCK_40__

Browser support

__INLINE_CODE_99__ supports all major browsers (Chrome, Edge, Firefox, Safari).

To support IE 11 and older browsers, make sure to use a tool like Babel to transform your code into code that works in the browsers you target.

Contributing

Feel free to try it out and checkout the examples. If you wanna fix something feel free to open a issue or a PR.

Backers

Thank you to all our backers!

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website.

Keywords