npm.io
15.0.2 • Published 3 weeks ago

@furystack/shades

Licence
GPL-2.0
Version
15.0.2
Deps
5
Size
1.6 MB
Vulns
0
Weekly
0
Stars
5

@furystack/shades

Shades is a UI library for FuryStack that uses type-safe JSX components, unidirectional data binding, and the same DI/IoC, logging, and utility libraries as FuryStack backend services.

Installation

npm install @furystack/shades
# or
yarn add @furystack/shades

Usage

You can check the @furystack/boilerplate repository for a working example.

A Shade (Component)

A shade (component) can be constructed from the following properties:

  • render:(options: RenderOptions)=>JSX.Element – A required method that will be executed on each render. Use useDisposable within render for one-time setup that needs cleanup.
  • customElementName – The custom element tag name. Must follow Custom Elements naming convention (lowercase, must contain a hyphen).
  • style – Optional inline styles applied to each component instance. Use for per-instance overrides.
  • css – Optional CSS styles injected as a stylesheet during component registration. Supports pseudo-selectors and nested selectors.
Styling

Shades provides two complementary approaches to styling components:

style Property (Inline Styles)

The style property applies inline styles to each component instance. Use this for:

  • Per-instance style overrides
  • Dynamic styles that change based on props/state
  • Quick prototyping
const MyComponent = Shade({
  customElementName: 'my-component',
  style: {
    display: 'flex',
    padding: '16px',
  },
  render: () => <div>Content</div>,
})

// Override styles on specific instances
<MyComponent style={{ marginTop: '20px' }} />
css Property (Stylesheet Injection)

The css property injects CSS rules into a stylesheet once per component type. Use this for:

  • Component-level default styles
  • Pseudo-selectors (:hover, :active, :focus, :disabled, etc.)
  • Nested selectors (child elements, class names)
  • Better performance (styles injected once, not per-instance)
const Button = Shade({
  customElementName: 'my-button',
  css: {
    padding: '12px 24px',
    backgroundColor: 'blue',
    color: 'white',
    border: 'none',
    cursor: 'pointer',
    transition: 'all 0.2s ease',

    '&:hover': {
      backgroundColor: 'darkblue',
    },

    '&:active': {
      transform: 'scale(0.98)',
    },

    '&:disabled': {
      opacity: '0.5',
      cursor: 'not-allowed',
    },

    '& .icon': {
      marginRight: '8px',
    },
  },
  render: ({ props }) => (
    <button disabled={props.disabled}>
      {props.icon && <span className="icon">{props.icon}</span>}
      {props.children}
    </button>
  ),
})
When to Use Which
Use Case style css
Hover/focus/active states
Per-instance overrides
Nested element styling
Dynamic values from props
Component defaults

Both properties can be used together. Inline style will override css due to CSS specificity rules.

Render Options

The render function receives a RenderOptions object with these hooks:

  • props – The current readonly props object. Passed from the parent, treat as immutable.
  • injector – The injector instance, inherited from the closest parent or set explicitly.
  • children – The children element(s) of the component.
  • renderCount – How many times this component has rendered.
  • useState(key, initialValue) – Local state that triggers re-renders on change.
  • useObservable(key, observable, options?) – Subscribes to an ObservableValue; re-renders on change by default. Provide a custom onChange to skip re-renders.
  • useSearchState(key, initialValue) – State synced with URL search parameters.
  • useStoredState(key, initialValue, storageArea?) – State persisted to localStorage or sessionStorage.
  • useDisposable(key, factory) – Creates a resource that is automatically disposed when the component unmounts.
  • useHostProps(hostProps) – Declaratively sets attributes, styles, and CSS variables on the host element. Prefer this over direct DOM manipulation.
  • useRef(key) – Creates a ref object for imperative access to child DOM elements.
Bundled Goodies

The @furystack/shades package contains a router component, a router-link component, a location service, and a lazy-load component.

Core Concepts

  • Shade is close to the DOM and the natives. You are encouraged to use native browser methods when possible.
  • You can use small independent services for state tracking with the injector.
  • You can use resources (value observers) that will be disposed automatically when your component is removed from the DOM.
  • Re-rendering can be skipped on state update. For example, why re-render a whole form if only one field has changed?
  • Nothing is true, everything is permitted.