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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jackens/nnn

v2026.11.17

Published

Jackens’ JavaScript helpers.

Readme

nnn

A collection of Jackens’ JavaScript helper utilities (version: 2026.11.17).

Installation

bun i @jackens/nnn

or

npm i @jackens/nnn

Usage

import {
  type CNode,
  type HArgs,
  type HArgs1,
  c,
  h,
  rwd,
  s,
  svgUse,
} from '@jackens/nnn' // or './node_modules/@jackens/nnn/nnn.js'

Exports

  • CNode: Represents a CSS rule node for the c helper. Keys are CSS properties or nested selectors.
  • HArgs: Tuple argument type for the h and s helpers.
  • HArgs1: Single argument type for the h and s helpers.
  • c: A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.
  • h: A lightweight HyperScript-style helper for creating and modifying HTMLElements (see also s).
  • rwd: A responsive web design helper that generates CSS rules for a grid-like layout of floating elements.
  • s: A lightweight HyperScript-style helper for creating and modifying SVGElements (see also h).
  • svgUse: Shorthand for creating an <svg> element with a <use> child referencing a symbol (e.g. an icon) by ID.

CNode

type CNode = {
  [attributeOrSelector: string]: string | number | CNode | null | undefined
}

Represents a CSS rule node for the c helper. Keys are CSS properties or nested selectors.

HArgs

type HArgs = [string | Node, ...HArgs1[]]

Tuple argument type for the h and s helpers.

HArgs1

type HArgs1 = Record<PropertyKey, unknown> | null | undefined | Node | string | number | HArgs

Single argument type for the h and s helpers.

c

const c: (node: CNode, splitter?: string) => string

A minimal CSS-in-JS helper that converts a JavaScript object hierarchy into a CSS string.

node

An object describing CSS rules. Keys are selectors or at-rules; values are either CSS property values or nested rule objects. Nested selectors are appended to the parent selector as is (e.g. '.c' inside a → a.c), so a leading space is needed for a descendant combinator (e.g. ' .c' → a .c). If a nested selector contains &, every & is replaced with the parent selector instead (e.g. '&:hover' → a:hover, '.c &' → .c a). A selector key may contain comma-separated alternatives (e.g. '.b,.c'); when nested, each alternative is combined with each parent alternative (cartesian product), producing a comma-joined selector list (e.g. 'a.b,a.c'). Commas inside brackets or quotes (e.g. ':is(.b,.c)', '[title="b,c"]') do not separate alternatives. Whitespace after a separating comma belongs to the next alternative, so like a leading space of the first one it makes a descendant combinator when nested (e.g. ' .b, .c' → a .b,a .c, '.b, .c' → a.b,a .c); leading whitespace of a resulting selector is ignored (e.g. 'h1, h2' → h1,h2, '.b, &:hover' → a.b,a:hover). At-rules (e.g. '@media …') wrap their nested rules; an at-rule nested in a selector wraps that selector (e.g. { a: { '@media(…)': { color: 'red' } } } → @media(…){a{color:red}}). Inside a top-level :root rule nested keys are concatenated into property names (e.g. { ':root': { '--': { bg: 'red' } } } → :root{--bg:red}). Properties without a selector (top-level or directly in a top-level at-rule, e.g. @font-face) are emitted as bare declarations. null/undefined values are skipped.

splitter

A delimiter used to create unique keys (default: '$$'). The substring from splitter to the end of a key is ignored (e.g. src$$1 → src). An empty splitter disables this feature.

Returns

A CSS string representing the compiled rules.

Usage Examples

Nested selectors split a rule while preserving property order:

const actual = c({
  a: {
    color: 'red',
    margin: 1,
    '.c': { margin: 2, padding: 2 },
    padding: 1,
  },
})

const expected = `
  a{
    color:red;
    margin:1
  }
  a.c{
    margin:2;
    padding:2
  }
  a{
    padding:1
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

Selectors can be nested at any depth:

const actual = c({
  a: {
    '.b': {
      color: 'red',
      margin: 1,
      '.c': { margin: 2, padding: 2 },
      padding: 1,
    },
  },
})

const expected = `
  a.b{
    color:red;
    margin:1
  }
  a.b.c{
    margin:2;
    padding:2
  }
  a.b{
    padding:1
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

At-rules, keyframes, media queries and repeated keys with the $$ suffix:

const actual = c({
  '@font-face$$1': {
    'font-family': 'Jackens',
    src$$1: 'url(otf/jackens.otf)',
    src$$2: "url(otf/jackens.otf) format('opentype'),url(svg/jackens.svg) format('svg')",
    'font-weight': 'normal',
    'font-style': 'normal',
  },
  '@font-face$$2': {
    'font-family': 'C64',
    src: 'url(fonts/C64_Pro_Mono-STYLE.woff)',
  },
  '@keyframes spin': {
    '0%': { transform: 'rotate(0deg)' },
    '100%': { transform: 'rotate(360deg)' },
  },
  div: {
    border: 'solid red 1px',
    '.c1': { 'background-color': '#000' },
    ' .c1': { 'background-color': 'black' },
    '.c2': { 'background-color': 'rgb(0,0,0)' },
  },
  '@media(min-width:200px)': {
    div: { margin: 0, padding: 0 },
    span: { color: '#000' },
  },
})

const expected = `
  @font-face{
    font-family:Jackens;
    src:url(otf/jackens.otf);
    src:url(otf/jackens.otf) format('opentype'),url(svg/jackens.svg) format('svg');
    font-weight:normal;
    font-style:normal
  }
  @font-face{
    font-family:C64;
    src:url(fonts/C64_Pro_Mono-STYLE.woff)
  }
  @keyframes spin{
    0%{
      transform:rotate(0deg)
    }
    100%{
      transform:rotate(360deg)
    }
  }
  div{
    border:solid red 1px
  }
  div.c1{
    background-color:#000
  }
  div .c1{
    background-color:black
  }
  div.c2{
    background-color:rgb(0,0,0)
  }
  @media(min-width:200px){
    div{
      margin:0;
      padding:0
    }
    span{
      color:#000
    }
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

Comma-separated nested selectors are expanded:

const actual = c({
  a: {
    '.b,.c': {
      margin: 1,
      '.d': {
        margin: 2,
      },
    },
  },
})

const expected = `
  a.b,a.c{
    margin:1
  }
  a.b.d,a.c.d{
    margin:2
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

Comma-separated top-level selectors are expanded in nested rules:

const actual = c({
  '.b,.c': {
    margin: 1,
    '.d': {
      margin: 2,
    },
  },
})

const expected = `
  .b,.c{
    margin:1
  }
  .b.d,.c.d{
    margin:2
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

Comma-separated selectors at both levels produce all combinations:

const actual = c({
  '.a,.b': {
    margin: 1,
    '.c,.d': {
      margin: 2,
    },
  },
})

const expected = `
  .a,.b{
    margin:1
  }
  .a.c,.a.d,.b.c,.b.d{
    margin:2
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

CSS custom properties from nested -- objects:

const actual = c({
  ':root$$monokai': {
    '--': {
      bg: '#faf4f2',
      fg: '#29242a',
      comment: '#918c8e',
      identifier: {
        1: '#7058be',
        2: '#269d69',
        3: '#1c8ca8',
        4: '#29242a',
      },
      keyword: {
        1: '#e14775',
        2: '#7058be',
        3: '#1c8ca8',
      },
      number: '#7058be',
      operator: '#e14775',
      punctuation: '#918c8e',
      string: '#cc7a0a',
    },
  },
  '@media only screen and (prefers-color-scheme: dark)$$monokai': {
    ':root': {
      '--': {
        bg: '#2d2a2e',
        fg: '#fcfcfa',
        comment: '#727072',
        identifier: {
          1: '#ae81ff',
          2: '#a9dc76',
          3: '#66d9ef',
          4: '#fcfcfa',
        },
        keyword: {
          1: '#ff6188',
          2: '#ae81ff',
          3: '#66d9ef',
        },
        number: '#ae81ff',
        operator: '#ff6188',
        punctuation: '#727072',
        string: '#ffd866',
      },
    },
  },
})

const expected = `
  :root{
    --bg:#faf4f2;
    --fg:#29242a;
    --comment:#918c8e;
    --identifier1:#7058be;
    --identifier2:#269d69;
    --identifier3:#1c8ca8;
    --identifier4:#29242a;
    --keyword1:#e14775;
    --keyword2:#7058be;
    --keyword3:#1c8ca8;
    --number:#7058be;
    --operator:#e14775;
    --punctuation:#918c8e;
    --string:#cc7a0a
  }
  @media only screen and (prefers-color-scheme: dark){
    :root{
      --bg:#2d2a2e;
      --fg:#fcfcfa;
      --comment:#727072;
      --identifier1:#ae81ff;
      --identifier2:#a9dc76;
      --identifier3:#66d9ef;
      --identifier4:#fcfcfa;
      --keyword1:#ff6188;
      --keyword2:#ae81ff;
      --keyword3:#66d9ef;
      --number:#ae81ff;
      --operator:#ff6188;
      --punctuation:#727072;
      --string:#ffd866
    }
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

& is replaced with the parent selector:

const actual = c({
  a: {
    color: 'red',
    '&:hover': { color: 'blue' },
    '.dark &': { color: 'white' },
    '.b,& .c': { margin: 1 },
  },
})

const expected = `
  a{
    color:red
  }
  a:hover{
    color:blue
  }
  .dark a{
    color:white
  }
  a.b,a .c{
    margin:1
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

Commas inside brackets or quotes do not split selectors:

const actual = c({
  ':is(.a,.b)': {
    '.c,.d': { margin: 1 },
    '[title="x,y"]': { margin: 2 },
  },
})

const expected = `
  :is(.a,.b).c,:is(.a,.b).d{
    margin:1
  }
  :is(.a,.b)[title="x,y"]{
    margin:2
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

Whitespace after a comma is kept, but leading whitespace of a resulting selector is ignored:

const actual = c({
  'h1, h2': {
    margin: 0,
    ' .a, .b': { margin: 1 },
    '.c, .d , &:hover': { margin: 2 },
  },
})

const expected = `
  h1,h2{
    margin:0
  }
  h1 .a,h1 .b,h2 .a,h2 .b{
    margin:1
  }
  h1.c,h1 .d,h1:hover,h2.c,h2 .d,h2:hover{
    margin:2
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

At-rules nested in selectors wrap these selectors:

const actual = c({
  a: {
    color: 'red',
    '@media(min-width:600px)': {
      color: 'blue',
      '.b': { margin: 1 },
    },
  },
})

const expected = `
  a{
    color:red
  }
  @media(min-width:600px){
    a{
      color:blue
    }
    a.b{
      margin:1
    }
  }
`.replace(/\n\s*/g, '')

expect(actual).toBe(expected)

Declarations without a selector are separated from subsequent rules:

expect(c({ color: 'red', margin: 0 })).toBe('color:red;margin:0')

expect(c({ color: 'red', a: { margin: 0 }, padding: 0 })).toBe('color:red;a{margin:0}padding:0')

null and undefined values are skipped:

const actual = c({
  a: {
    color: 'red',
    margin: null,
    padding: undefined,
    '.b': null,
  },
  ':root': {
    '--': { bg: null, fg: 'black' },
  },
})

expect(actual).toBe('a{color:red}:root{--fg:black}')

Custom splitter (an empty one disables splitting):

expect(c({ a: { 'src#1': 'url(a.otf)', 'src#2': 'url(a.woff)' } }, '#')).toBe('a{src:url(a.otf);src:url(a.woff)}')

expect(c({ a: { b$$c: 1 } }, '')).toBe('a{b$$c:1}')

h

const h: {
  <T extends keyof HTMLElementTagNameMap>(tag: T, ...args: HArgs1[]): HTMLElementTagNameMap[T]
  <N extends Node>(node: N, ...args: HArgs1[]): N
  (tag: string, ...args: HArgs1[]): HTMLElement
  (node: Node, ...args: HArgs1[]): Node
}

A lightweight HyperScript-style helper for creating and modifying HTMLElements (see also s).

tagOrNode

If a string, it is treated as the tag name for a new element. If a Node, that node is modified in place.

args

Additional arguments processed as follows:

  • Object: maps attributes/properties.
    • Keys starting with $ set element properties (without the $ prefix); if the value is a plain object (created by {…} or Object.create(null)), it is shallow-merged into the existing property instead of replacing it (useful e.g. for $style), other values (including arrays and class instances) replace the property.
    • Other keys set attributes: a key prefixed with xlink:, xml: or xmlns: sets the attribute via setAttributeNS in the corresponding namespace; any other key sets it via setAttribute. A value of true sets an empty attribute (e.g. checked=""), a value of false removes the attribute, a value of null/undefined leaves the attribute unchanged.
  • null/undefined: ignored.
  • Node: appended as a child.
  • string/number: converted to a Text node and appended.
  • HArgs array: processed recursively (creates or modifies a child node and appends it).

Returns

The created HTMLElement or the modified node.

Usage Examples

Creating elements, appending children and setting properties:

const b = h('b')

expect(b.outerHTML).toBe('<b></b>')

const i = h('i', 'text')

h(b, i)

expect(i.outerHTML).toBe('<i>text</i>')
expect(b.outerHTML).toBe('<b><i>text</i></b>')

h(i, { $className: 'some class' })

expect(i.outerHTML).toBe('<i class="some class">text</i>')
expect(b.outerHTML).toBe('<b><i class="some class">text</i></b>')

Text content from strings, numbers and $innerText:

expect(h('span', 'text').outerHTML).toBe('<span>text</span>')
expect(h('span', { $innerText: 'text' }).outerHTML).toBe('<span>text</span>')

expect(h('span', '42').outerHTML).toBe('<span>42</span>')
expect(h('span', 42).outerHTML).toBe('<span>42</span>')

style attribute vs $style property (string or object):

expect(h('div', { style: 'margin:0;padding:0' }).outerHTML).toBe('<div style="margin:0;padding:0"></div>')
expect(h('div', { $style: 'margin:0;padding:0' }).outerHTML).toBe('<div style="margin: 0px; padding: 0px;"></div>')
expect(h('div', { $style: { margin: 0, padding: 0 } }).outerHTML).toBe(
  '<div style="margin: 0px; padding: 0px;"></div>'
)

value attribute vs $value property:

const input1 = h('input', { value: 42 })
const input2 = h('input', { $value: '42' })

expect(input1.value).toBe('42')
expect(input2.value).toBe('42')

expect(input1.outerHTML).toBe('<input value="42">')
expect(input2.outerHTML).toBe('<input>')

checked attribute vs $checked property:

const checkbox1 = h('input', {
  type: 'checkbox',
  checked: true,
})

const checkbox2 = h('input', {
  type: 'checkbox',
  $checked: true,
})

expect(checkbox1.checked).toBe(true)
expect(checkbox2.checked).toBe(true)

expect(checkbox1.outerHTML).toBe('<input type="checkbox" checked="">')
expect(checkbox2.outerHTML).toBe('<input type="checkbox">')

Object values of $-prefixed properties are merged:

const div = h('div') as any

expect(div.key).toBeUndefined()

h(div, { $key: { one: 1 } })

expect(div.key).toEqual({ one: 1 })

h(div, { $key: { two: 2 } })

expect(div.key).toEqual({ one: 1, two: 2 })

Class attribute, appending text and nested children given as arrays:

const elemWithClass = h('div', { class: 'test' })

expect(elemWithClass.getAttribute('class')).toBe('test')

const elemWithText = h('div', 'initial')

h(elemWithText, ' more')

expect(elemWithText.outerHTML).toBe('<div>initial more</div>')

const elemWithNested = h('div', ['span', 'hello'], ['b', 'world'])

expect(elemWithNested.outerHTML).toBe('<div><span>hello</span><b>world</b></div>')

false removes an attribute, null and undefined leave it unchanged:

const input = h('input', { type: 'checkbox', checked: true, title: 'title' })

h(input, { checked: false, title: null, type: undefined })

expect(input.outerHTML).toBe('<input type="checkbox" title="title">')

Only plain objects are merged into $-prefixed properties:

const date = new Date(0)
const div = h('div', { $date: date, $list: [1, 2] }) as any

expect(div.date).toBe(date)

h(div, { $list: [3] })

expect(div.list).toEqual([3])

Namespaced attributes:

const div = h('div', { 'xml:lang': 'pl', 'ng:x': 1 })

expect(div.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')).toBe('pl')
expect(div.getAttribute('ng:x')).toBe('1')

Return types:

expectTypeOf(h('input')).toEqualTypeOf<HTMLInputElement>()
expectTypeOf(h('my-element')).toEqualTypeOf<HTMLElement>()
expectTypeOf(h(document.createTextNode(''))).toEqualTypeOf<Text>()

rwd

const rwd: (
  root: CNode,
  selector: string,
  cellWidthPx: number,
  cellHeightPx: number,
  ...specs: [columns: number, width?: number, height?: number][]
) => void

A responsive web design helper that generates CSS rules for a grid-like layout of floating elements. It modifies root in place: it sets base styles for selector and adds a @media(min-width:…px) rule for each breakpoint.

root

The CSS root object to populate (see c).

selector

The CSS selector for the grid item.

cellWidthPx

The base cell width in pixels (a positive number).

cellHeightPx

The base cell height in pixels (a positive number).

specs

An array of breakpoint specifications, each a tuple of positive integers:

  • columns: number of cells per row; the breakpoint is min-width: cellWidthPx * columns (1 modifies the base rule).
  • width (optional, default 1, at most columns): number of horizontal cells the element spans.
  • height (optional, default 1): number of vertical cells the element spans.

Throws

A RangeError if any of the numeric arguments is out of range.

Usage Examples

Responsive grid cell widths and heights with media queries:

const style: CNode = {
  body: {
    margin: 0,
  },
  '.r6': {
    border: 'solid red 1px',
    '.no-border': {
      border: 'none',
    },
  },
}

rwd(style, '.r6', 200, 50, [6], [3], [1, 1, 2])

expect(style).toEqual({
  body: {
    margin: 0,
  },
  '.r6': {
    border: 'solid red 1px',
    '.no-border': {
      border: 'none',
    },
    'box-sizing': 'border-box',
    display: 'block',
    float: 'left',
    width: '100%',
    height: '100px',
  },
  '@media(min-width:600px)': {
    '.r6': {
      width: 'calc(100% / 3)',
      height: '50px',
    },
  },
  '@media(min-width:1200px)': {
    '.r6': {
      width: 'calc(50% / 3)',
      height: '50px',
    },
  },
})

Widths that divide evenly do not use calc; existing at-rules are extended:

const style: CNode = {
  '@media(min-width:400px)': {
    '.r2': { margin: 0 },
  },
}

rwd(style, '.r2', 100, 40, [4, 2], [2, 2, 3])

expect(style).toEqual({
  '@media(min-width:400px)': {
    '.r2': {
      margin: 0,
      width: '50%',
      height: '40px',
    },
  },
  '.r2': {
    'box-sizing': 'border-box',
    display: 'block',
    float: 'left',
    width: '100%',
    height: '40px',
  },
  '@media(min-width:200px)': {
    '.r2': {
      width: '100%',
      height: '120px',
    },
  },
})

Invalid arguments throw a RangeError:

expect(() => rwd({}, '.x', 0, 50)).toThrow(RangeError)
expect(() => rwd({}, '.x', 100, Infinity)).toThrow(RangeError)
expect(() => rwd({}, '.x', 100, 50, [0])).toThrow(RangeError)
expect(() => rwd({}, '.x', 100, 50, [2, 3])).toThrow(RangeError)
expect(() => rwd({}, '.x', 100, 50, [2, 1, 1.5])).toThrow(RangeError)

s

const s: {
  <T extends keyof SVGElementTagNameMap>(tag: T, ...args: HArgs1[]): SVGElementTagNameMap[T]
  <N extends Node>(node: N, ...args: HArgs1[]): N
  (tag: string, ...args: HArgs1[]): SVGElement
  (node: Node, ...args: HArgs1[]): Node
}

A lightweight HyperScript-style helper for creating and modifying SVGElements (see also h).

tagOrNode

If a string, it is treated as the tag name for a new element. If a Node, that node is modified in place.

args

Additional arguments processed as follows:

  • Object: maps attributes/properties.
    • Keys starting with $ set element properties (without the $ prefix); if the value is a plain object (created by {…} or Object.create(null)), it is shallow-merged into the existing property instead of replacing it, other values (including arrays and class instances) replace the property.
    • Other keys set attributes: a key prefixed with xlink:, xml: or xmlns: sets the attribute via setAttributeNS in the corresponding namespace (e.g. xml:lang); any other key sets it via setAttribute. A value of true sets an empty attribute, a value of false removes the attribute, a value of null/undefined leaves the attribute unchanged.
  • null/undefined: ignored.
  • Node: appended as a child.
  • string/number: converted to a Text node and appended.
  • HArgs array: processed recursively (creates or modifies a child node and appends it).

Returns

The created SVGElement or the modified node.

Usage Examples

Elements (including nested ones) are created in the SVG namespace:

const svg = s('svg', ['g', ['circle', { r: 1 }]])

expect(svg.namespaceURI).toBe('http://www.w3.org/2000/svg')
expect(svg.querySelector('circle')?.namespaceURI).toBe('http://www.w3.org/2000/svg')
expect(svg.outerHTML).toBe('<svg><g><circle r="1"></circle></g></svg>')

Namespaced xlink: attribute set to true gets an empty value:

const svg = s('svg', { 'xlink:href': true })

expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('')

Namespaced xlink: attribute set to false is removed:

const svg = s('svg', { 'xlink:href': '#id' })

s(svg, { 'xlink:href': false })

expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBeNull()

Namespaced xlink: attribute with a string value:

const svg = s('svg', { 'xlink:href': 'http://example.com' })

expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('http://example.com')

Namespaced xlink: attribute with a number value:

const svg = s('svg', { 'xlink:title': 42 })

expect(svg.getAttributeNS('http://www.w3.org/1999/xlink', 'title')).toBe('42')

Namespaced xml: and xmlns: attributes:

const svg = s('svg', { 'xml:lang': 'pl', 'xmlns:xlink': 'http://www.w3.org/1999/xlink' })

expect(svg.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')).toBe('pl')
expect(svg.getAttributeNS('http://www.w3.org/2000/xmlns/', 'xlink')).toBe('http://www.w3.org/1999/xlink')

Return types:

expectTypeOf(s('circle')).toEqualTypeOf<SVGCircleElement>()
expectTypeOf(s('my-element')).toEqualTypeOf<SVGElement>()

svgUse

const svgUse: (id: string, ...args: HArgs1[]) => SVGSVGElement

Shorthand for creating an <svg> element with a <use> child referencing a symbol (e.g. an icon) by ID.

Equivalent to:

s('svg', ['use', { href: '#' + id }], ...args)

id

The ID of the symbol to reference (without the # prefix).

args

Additional arguments passed to the outer <svg> element.

Returns

An SVGSVGElement containing a <use> element.

Usage Examples

SVG use element referencing a symbol:

const svg = svgUse('icon-home')

expect(svg.outerHTML).toBe('<svg><use href="#icon-home"></use></svg>')

Additional attributes are set on the outer svg element:

const svg = svgUse('icon-settings', { width: 24, height: 24, class: 'icon' })

expect(svg.outerHTML).toBe('<svg width="24" height="24" class="icon"><use href="#icon-settings"></use></svg>')

Additional children are appended to the outer svg element:

const svg = svgUse('icon-star', ['title', 'Star'])

expect(svg.outerHTML).toBe('<svg><use href="#icon-star"></use><title>Star</title></svg>')

License

The MIT License (MIT)

Copyright (c) 2016+ Jackens

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.