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

@embellish/react

v0.7.1

Published

A lightweight React styling primitive, powered entirely by CSS and inline styles

Downloads

21

Readme


Embellish offers a powerful and intuitive way to style your React application deterministically, without runtime style injection or extra build steps. Easily create a polymorphic component with your own custom style props, and use a pure CSS mechanism to apply styles conditionally, creating hover effects, responsive behavior, and more. Consider this example of a Box component:

<Box
  as="a"
  href="https://github.com/embellishing/embellish"
  color="#fff"
  background="#03f"
  hover:background="color-mix(in srgb, #03f, #fff 12.5%)"
  active:background="#c30">
  Get started
</Box>

With a styling API that builds upon the declarative nature of React, the Box component enables you to manage complex styling scenarios with ease. Meanwhile, Embellish's purely CSS-driven approach for defining conditions like hover and active means that you can create dynamic and interactive UI elements without compromising on performance or maintainability.

Features

  • Conditional styles with pure CSS: Under the hood, Embellish uses CSS Variables to apply styles conditionally based on pseudo-classes, at-rules, or arbitrary selector logic.
  • First-class style props: Components expose CSS properties as first-class props. You can choose which ones to support or even define your own custom props.
  • Style prop conditions: Specify the name of a condition as a style prop modifier, e.g. hover:background="#333", and its value will apply only under that condition.
  • Inline conditions: Conditions can be combined inline using logical operators, providing flexibility, promoting reuse, and keeping global CSS to a minimum.
  • No runtime style injection: Avoid hydration mismatches, flashes of unstyled content, and questionable performance of runtime style injection.
  • No build step: Simplify the development workflow by avoiding static analysis and extra build steps.
  • Near-perfect code splitting: Most style information is embedded directly in component markup, with a minimal global style sheet used only to define reusable conditions.
  • No cascade defects: Embellish's use of inline styles ensures that CSS rulesets can't "leak in" and modify private component implementation details.

Installation

Install @embellish/react using your package manager of choice, e.g.

npm install @embellish/react

Getting started

Step 1: CSS hooks

Step 1a: Define hooks

Start by defining CSS hooks. These are all of the "selectors" you want to use throughout your app. These can be actual CSS selectors or even at-rules.

import { createHooks } from "@embellish/react";

const { StyleSheet, hooks } = createHooks([
  "&:hover",
  "&:focus",
  "&:active",
  "&:disabled",
  "&[aria-disabled=true]",
  "@media (width >= 600px)",
]);

[!NOTE]

It's a good practice to keep these hooks as simple and generic as possible to promote reuse. Later, you can combine them to create more complex conditions.

Step 1b: Add style sheet

The StyleSheet component obtained in the previous step renders a small static style sheet containing the CSS required to support conditional styling. Add this to the root layout component or entry point of your app.

// e.g. src/main.tsx

root.render(
  <StrictMode>
+   <StyleSheet />
    <App />
  </StrictMode>
);

Step 2: Create reusable conditions

A reusable condition assigns an alphanumeric alias (i.e. a valid prop name) to each hook. You can also define complex conditions using logical operators.

import { createConditions } from "@embellish/react";

const conditions = createConditions(hooks, {
  hover: "&:hover",
  focus: "&:focus",
  active: "&:active",
  disabled: { or: ["&:disabled", "&[aria-disabled=true]"] },
  intent: { or: ["&:hover", "&:focus"] },
  desktop: "@media (width >= 600px)",
});

[!NOTE]

At this stage, it's still a good practice to consider the reusability of each complex condition defined here. You can define inline conditions later for one-off use cases.

Step 3: Create style props

Use the createStyleProps function to define style props. The keys of the configuration object are the prop names, with each entry consisting of either

  • a function, parameterized by the prop value, which returns a React CSSProperties object; or
  • true, indicating that a default implementation should be used (standard CSS properties only)
import { createStyleProps } from "@embellish/react";

const styleProps = createStyleProps({
  backgroundColor: (value: CSSProperties["backgroundColor"]) => ({
    backgroundColor: value,
  }), // defined as a function for illustrative purposes
  border: true,
  borderRadius: true,
  color: true,
  cursor: true,
  display: true,
  fontSize: true,
  fontWeight: true,
  outline: true,
  outlineOffset: true,
  padding: true,
  transition: true,
});

Step 4: Create a component

Create a Box component using the conditions defined in the previous step along with your desired style props.

import { createComponent } from "@embellish/react";

const Box = createComponent({
  displayName: "Box", // recommended for debugging purposes
  defaultAs: "div", // optional, any HTML tag or component
  defaultStyle: () => ({
    // optional, a regular React style object consisting of "base" styles
    boxSizing: "border-box",
    textDecoration: "none",
  }),
  conditions,
  styleProps,
});

Step 5: Use the component

Use your Box component to create a styled button:

function CtaButton({
  href,
  children,
  disabled,
}: {
  href: string;
  children?: ReactNode;
  disabled?: boolean;
}) {
  return (
    <Box
      as="a"
      href={href}
      aria-disabled={disabled}
      display="inline-block"
      backgroundColor="#6200ea"
      color="#ffffff"
      padding="12px 24px"
      border="none"
      borderRadius="4px"
      cursor="pointer"
      fontSize="16px"
      fontWeight="bold"
      transition="background-color 0.3s, color 0.3s"
      intent:backgroundColor="#3700b3"
      active:backgroundColor="#6200ea"
      active:color="#bb86fc"
      focus:outline="2px solid #03dac6"
      focus:outlineOffset="2px"
      disabled:cursor="not-allowed">
      {children}
    </Box>
  );
}

Advanced usage

Inline conditions

You can compose conditions inline using logical operators, creating maximum flexibility and reuse for the hooks you defined in Step 1 above. Simply pass additional conditions to the conditions prop, and then use them as style prop modifiers:

function CtaButton({
  href,
  children,
  disabled,
}: {
  href: string;
  children?: ReactNode;
  disabled?: boolean;
}) {
  return (
    <Box
+     conditions={{
+       intentEnabled: {
+         and: ["intent", { not: "disabled" }],
+       },
+       activeEnabled: {
+         and: ["active", { not: "disabled" }],
+       },
+       focusEnabled: {
+         and: ["focus", { not: "disabled" }],
+       },
+     }}
      as="a"
      href={href}
      aria-disabled={disabled}
      display="inline-block"
      backgroundColor="#6200ea"
      color="#ffffff"
      padding="12px 24px"
      border="none"
      borderRadius="4px"
      cursor="pointer"
      fontSize="16px"
      fontWeight="bold"
      transition="background-color 0.3s, color 0.3s"
-     intent:backgroundColor="#3700b3"
-     active:backgroundColor="#6200ea"
-     active:color="#bb86fc"
-     focusEnabled:outline="2px solid #03dac6"
+     intentEnabled:backgroundColor="#3700b3"
+     activeEnabled:backgroundColor="#6200ea"
+     activeEnabled:color="#bb86fc"
+     focusEnabled:outline="2px solid #03dac6"
      focus:outlineOffset="2px"
      disabled:cursor="not-allowed">
      {children}
    </Box>
  );
}

Browser support

| Chrome | Edge | Safari | Firefox | Opera | | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | 49+ | 16+ | 10+ | 31+ | 36+ |

Contributing

Contributions are welcome. Please see the contributing guidelines for more information.

License

Embellish is offered under the MIT license.