Plumeria logoPlumeria
API ReferenceJavaScript API

css.marker / css.extended

export type marker = (id: string, pseudo: string) => Marker;
export const marker: marker;

export type extended = <I extends string, P extends string>(id: I, pseudo: P) => Extended<I, P>;
export const extended: extended;

The marker and extended APIs enable context-aware styling without relying on rigid DOM hierarchies (such as strict parent-child nesting) or combinator syntax. These paired APIs maintain atomicity while allowing child elements to respond to parent states.

Production Ready - These APIs compile to CSS Container Style Queries (@container style()). Since Firefox 151, style queries are fully supported across all major modern browsers (Chrome/Edge, Safari, and Firefox), making marker and extended completely stable for production use.

  • marker(id, pseudo) - Sets a CSS variable marker on a parent element
  • extended(id, pseudo) - Applies styles to children when the marker is active

Parameters

The parameters must be linked (match) in both cases.

  • id - A unique identifier linking the marker and extended styles
  • pseudo - The pseudo-class state (e.g., ':hover', ':focus', ':active')

Example

Grand Container (Hover me)
Parent Container (Hover me too)
Child Element
TypeScript
import * as css from '@plumeria/core';

const animation = css.keyframes({
  '0%': {
    scale: 1.2,
  },
  '50%': {
    scale: 1.3,
  },
  '100%': {
    scale: 1.2,
  },
});

const styles = css.create({
  grand: {
    display: 'flex',
    flexDirection: 'column',
    gap: 15,
    alignItems: 'center',
    justifyContent: 'center',
    width: '100%',
    minHeight: 250,
    padding: 20,
    border: '2px dashed #c4c4c466',
    borderRadius: 12,
    transition: 'all 0.3s',
    ...css.marker('grand', ':hover'),
  },
  parent: {
    display: 'flex',
    flexDirection: 'column',
    gap: 10,
    alignItems: 'center',
    justifyContent: 'center',
    width: '80%',
    minHeight: 120,
    padding: 10,
    backgroundColor: '#f5f5f7',
    border: '2px solid #c4c4c444',
    borderRadius: 8,
    transition: 'all 0.3s',
    ...css.marker('parent', ':hover'),
  },
  child: {
    padding: '8px 16px',
    fontWeight: 'bold',
    color: '#86868b',
    background: '#e8e8ed',
    borderRadius: 6,
    transition: 'all 0.3s',
    [css.extended('grand', ':hover')]: {
      color: '#0066cc',
      background: '#e1f0ff',
    },
    [css.extended('parent', ':hover')]: {
      color: '#ffffff',
      background: '#ff9500',
      scale: 1.2,
      ':hover': {
        background: 'skyblue',
        animationName: animation,
        animationDuration: '1.2s',
        animationTimingFunction: 'ease-in-out',
        animationIterationCount: 'infinite',
      },
    },
  },
  discription: {
    fontSize: '14px',
    color: '#86868b',
  },
});

const MarkerExtended = () => {
  return (
    <div styleName={styles.grand}>
      <span styleName={styles.discription}>Grand Container (Hover me)</span>
      <div styleName={styles.parent}>
        <span styleName={styles.discription}>Parent Container (Hover me too)</span>
        <div styleName={styles.child}>Child Element</div>
      </div>
    </div>
  );
};
Generated CSS
.xtkxh191:hover {
  --grand-hover: 1;
}

.xhcsnlrf:hover {
  --parent-hover: 1;
}

@container style(--grand-hover: 1) {
  .x84muc4x:not(#\#) {
    color: #06c;
  }

  .x2x6k4j2:not(#\#):not(#\#) {
    background-color: #e1f0ff;
  }
}

@container style(--parent-hover: 1) {
  .xr8ck8y9:not(#\#) {
    color: #fff;
  }

  .xb1yp2w2:not(#\#):not(#\#) {
    background-color: #ff9500;
  }

  .xewf73ay:not(#\#) {
    scale: 1.3;
  }

  .x8odz1r5:not(#\#):not(#\#) {
    animation-name: kf-xmortmuw;
  }

  .x0guwijo:not(#\#):not(#\#) {
    animation-duration: 1.2s;
  }

  .xek6euru:not(#\#):not(#\#) {
    animation-timing-function: ease-in-out;
  }

  .xwdc6dqv:not(#\#):not(#\#) {
    animation-iteration-count: infinite;
  }

  .xd9yj17o:not(#\#):hover {
    color: #0ff;
  }
}

How it works

Internal

marker() generates a CSS variable when the pseudo-class is active:

.parent:hover {
  --ul-hover: 1;
}

extended() wraps child styles in a container query:

@container style(--ul-hover: 1) {
  .child { /* styles */ }
}

The CSS variable acts as a signal that propagates to descendants without DOM structure dependency.

ESLint Rule

The no-combinator rule enforces this pattern by disallowing combinator syntax.

On this page