Skip to content

feature: Pre-configured framework-agnostic makeStyles & Co. #672

@aweebit

Description

@aweebit

Despite the fact only React usage is documented, @griffel/react is actually just a thin wrapper around the core functionality that is framework-agnostic (see also #370).

However, the ergonomics of using @griffel/core directly are questionable because while the React wrapper's makeStyles function returns a hook that ensures that the provided renderer and writing direction are used, @griffel/core's makeStyles returns a function that expects those context parameters to be provided as arguments so that it can spit out the actual class string:

const renderer = createDOMRenderer();
const dir = 'ltr';

const getStylesA = makeStyles({ root: { color: 'red' }});
const classesA = getStylesA({ renderer, dir });

const getStylesB = makeStyles({ root: { color: 'green' }});
const classesB = getStylesB({ renderer, dir });

To simplify vanilla usage, I suggest providing a Griffel class whose instances would include pre-configured makeStyles, makeResetStyles and makeStaticStyles functions that return the class strings directly. The code above could then be simplified to this:

const { makeStyles } = new Griffel();

const classesA = makeStyles({ root: { color: 'red' }});
const classesB = makeStyles({ root: { color: 'green' }});

You would of course be able to also provide your custom renderer and writing direction with new Griffel({ renderer, dir }), but if you didn't, a renderer would be created automatically with a createDOMRenderer() call and the left-to-right direction would be assumed. This would be in accordance with the default values of the React wrapper's RendererContext and TextDirectionContext.

Implementation is pretty straight-forward:

type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };

type TextDirection = "ltr" | "rtl";

interface GriffelOptions {
  insertionFactory?: GriffelInsertionFactory;
  renderer?: GriffelRenderer;
  dir?: TextDirection;
}

class Griffel
  implements WithRequired<GriffelOptions, "renderer" | "dir">
{
  insertionFactory?: GriffelInsertionFactory = undefined;
  renderer: GriffelRenderer = createDOMRenderer();
  dir: TextDirection = "ltr";

  constructor(options?: GriffelOptions) {
    Object.assign(this, options);
  }

  makeStyles = <Slots extends string | number>(
    stylesBySlots: StylesBySlots<Slots>
  ) => {
    const { insertionFactory, renderer, dir } = this;
    return makeStyles(stylesBySlots, insertionFactory)({ renderer, dir });
  };

  makeResetStyles = (styles: GriffelResetStyle) => {
    const { insertionFactory, renderer, dir } = this;
    return makeResetStyles(styles, insertionFactory)({ renderer, dir });
  };

  makeStaticStyles = (styles: GriffelStaticStyles | GriffelStaticStyles[]) => {
    const { insertionFactory, renderer } = this;
    return makeStaticStyles(styles, insertionFactory)({ renderer });
  };
}

This is the code I would have to write if I were to employ Griffel in non-React projects today. It would be great if @griffel/core itself included such a class, as it would make it truly ready for framework-agnostic usage.

I would like to hear feedback from maintainers and could then submit a pull request if this addition to the library is deemed reasonable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions