CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-styletron-react

React bindings for Styletron CSS-in-JS toolkit providing styled components and hooks

Pending
Overview
Eval results
Files

component-composition.mddocs/

Component Composition

Utilities for extending and modifying existing styled components with additional styles, transformations, and wrapper components. These functions enable composition and reuse of styled components.

Capabilities

With Style

Extends existing styled components with additional styles using deep merge.

/**
 * Extends existing styled components with additional styles using deep merge
 * @param component - Existing styled component to extend
 * @param style - Style object or function to merge with existing styles
 * @returns New styled component with merged styles
 */
function withStyle<Base extends StyletronComponent<any, any>, Props = {}>(
  component: Base,
  style: StyleObject | ((props: Props) => StyleObject)
): Base extends StyletronComponent<infer D, infer P>
  ? StyletronComponent<D, P & Props>
  : never;

Usage Examples:

import { styled, withStyle } from "styletron-react";

// Base button component
const BaseButton = styled("button", {
  padding: "8px 16px",
  border: "none",
  borderRadius: "4px",
  fontSize: "14px",
  ":hover": {
    opacity: 0.8,
  },
});

// Extend with additional styles (static)
const PrimaryButton = withStyle(BaseButton, {
  backgroundColor: "blue",
  color: "white",
});

// Extend with dynamic styles
const ConditionalButton = withStyle<typeof BaseButton, {$danger: boolean}>(
  BaseButton,
  (props) => ({
    backgroundColor: props.$danger ? "red" : "green",
    color: "white",
  })
);

// Deep merge example - hover styles are merged
const HoverButton = withStyle(BaseButton, {
  backgroundColor: "purple",
  ":hover": {
    backgroundColor: "darkpurple", // Merged with existing hover styles
  },
});

With Transform

Transforms styles of existing styled components using a transformation function.

/**
 * Transforms styles of existing styled components using a transformation function
 * @param component - Existing styled component to transform
 * @param transformer - Function that receives current style and props, returns new style
 * @returns New styled component with transformed styles
 */
function withTransform<
  Base extends StyletronComponent<any, any>,
  Props,
>(
  component: Base,
  transformer: (style: StyleObject, props: Props) => StyleObject
): Base extends StyletronComponent<infer D, infer P>
  ? StyletronComponent<D, P & Props>
  : never;

Usage Examples:

import { styled, withTransform } from "styletron-react";

const BaseButton = styled("button", {
  padding: "8px 16px",
  backgroundColor: "blue",
  color: "white",
});

// Transform existing styles based on props
const ScalableButton = withTransform(
  BaseButton,
  (style, props: {$scale: number}) => ({
    ...style,
    transform: `scale(${props.$scale})`,
    transformOrigin: "center",
  })
);

// Conditional transformation
const ToggleButton = withTransform(
  BaseButton,
  (style, props: {$active: boolean}) => ({
    ...style,
    backgroundColor: props.$active ? "green" : style.backgroundColor,
    fontWeight: props.$active ? "bold" : "normal",
  })
);

With Wrapper

Wraps styled components with additional wrapper components for layout or behavior modifications.

/**
 * Wraps styled components with additional wrapper components
 * @param component - Existing styled component to wrap
 * @param wrapper - Function that receives the component and returns a wrapper component
 * @returns New styled component with wrapper applied
 */
function withWrapper<Base extends StyletronComponent<any, any>, Props>(
  component: Base,
  wrapper: (
    component: Base
  ) => React.ComponentType<Props & React.ComponentProps<Base>>
): Base extends StyletronComponent<infer D, infer P>
  ? StyletronComponent<D, P & Props>
  : never;

Usage Examples:

import { styled, withWrapper } from "styletron-react";

const BaseButton = styled("button", {
  padding: "8px 16px",
  backgroundColor: "blue",
  color: "white",
});

// Wrap with container div
const ContainerButton = withWrapper(BaseButton, (StyledButton) => (props) => (
  <div className="button-container">
    <StyledButton {...props} />
  </div>
));

// Wrap with loading state
const LoadingButton = withWrapper(
  BaseButton,
  (StyledButton) => (props: {$loading?: boolean} & React.ComponentProps<typeof BaseButton>) => (
    <div style={{position: "relative"}}>
      <StyledButton {...props} disabled={props.$loading || props.disabled}>
        {props.$loading ? "Loading..." : props.children}
      </StyledButton>
    </div>
  )
);

// Complex wrapper with additional functionality
const TooltipButton = withWrapper(
  BaseButton,
  (StyledButton) => (props: {$tooltip?: string} & React.ComponentProps<typeof BaseButton>) => (
    <div title={props.$tooltip}>
      <StyledButton {...props} />
    </div>
  )
);

Composition Patterns

Chaining Compositions

Multiple composition functions can be chained together:

import { styled, withStyle, withTransform, withWrapper } from "styletron-react";

const BaseButton = styled("button", {
  padding: "8px 16px",
  border: "none",
});

// Chain multiple compositions
const EnhancedButton = withWrapper(
  withTransform(
    withStyle(BaseButton, {
      backgroundColor: "blue",
      color: "white",
    }),
    (style, props: {$size: "small" | "large"}) => ({
      ...style,
      padding: props.$size === "large" ? "12px 24px" : "6px 12px",
    })
  ),
  (StyledButton) => (props) => (
    <div className="button-wrapper">
      <StyledButton {...props} />
    </div>
  )
);

Style Inheritance

Compositions preserve and extend the type information and styling behavior:

// All composition functions maintain prop types
const TypedButton = styled<"button", {$variant: "primary" | "secondary"}>("button", {
  padding: "8px 16px",
});

// withStyle preserves and extends types
const ExtendedButton = withStyle<typeof TypedButton, {$size: "small" | "large"}>(
  TypedButton,
  (props) => ({
    fontSize: props.$size === "large" ? "16px" : "14px",
    backgroundColor: props.$variant === "primary" ? "blue" : "gray",
  })
);

// Usage with full type safety
<ExtendedButton $variant="primary" $size="large">
  Typed Button
</ExtendedButton>

Types

type WithStyleFn = {
  <Base extends StyletronComponent<any, any>, Props = {}>(
    component: Base,
    style: StyleObject | ((props: Props) => StyleObject)
  ): Base extends StyletronComponent<infer D, infer P>
    ? StyletronComponent<D, P & Props>
    : never;
};

type WithTransformFn = <
  Base extends StyletronComponent<any, any>,
  Props,
>(
  component: Base,
  style: (style: StyleObject, props: Props) => StyleObject
) => Base extends StyletronComponent<infer D, infer P>
  ? StyletronComponent<D, P & Props>
  : never;

type WithWrapperFn = <Base extends StyletronComponent<any, any>, Props>(
  component: Base,
  wrapper: (
    component: Base
  ) => React.ComponentType<Props & React.ComponentProps<Base>>
) => Base extends StyletronComponent<infer D, infer P>
  ? StyletronComponent<D, P & Props>
  : never;

Install with Tessl CLI

npx tessl i tessl/npm-styletron-react

docs

component-composition.md

context-provider.md

core-styling.md

index.md

react-hooks.md

tile.json