@dbcdk/react-components

中文文档
0.0.240发布于 2026/11/12ISCTypeScriptESM + CJS零依赖近 30 天下载 7748

Reusable React components for DBC projects

原始使用文档
文档目录21 节
  1. Purpose of the library
  2. Getting started
  3. 1) Install the package
  4. 2) Import global styles
  5. 3) Add ThemeScript in your root layout (Next.js example)
  6. 4) Switching theme in your application
  7. 5) Running multiple apps on the same origin
  8. Package integration
  9. Optional table integration
  10. Optional Markdown integration
  11. Next.js configuration
  12. Using Storybook
  13. Local Storybook
  14. In Storybook you can:
  15. Themes
  16. Accessibility (a11y)
  17. MIME type helpers
  18. Native selection controls
  19. Contributing
  20. License
  21. Group labels, heading sizes, and print content

DBC React Components

Reusable React components for DBC projects.

This library provides a shared, themeable component system used across DBC’s internal (and selected external) applications. It is designed to promote consistency, speed up development, and improve overall quality and accessibility.


Purpose of the library

The goals of this component library are:

  • Consistency
    Provide a unified look and feel across DBC’s digital products, especially internal tools.

  • Development speed
    Reduce development and maintenance time by reusing well-tested components instead of rebuilding UI from scratch.

  • Quality & accessibility
    Components are reviewed and built according to best practices, with accessibility and robustness in mind, ensuring a strong baseline quality.

  • Reduced third-party dependency
    Increase digital independence by building and sharing our own components across the organisation, reducing reliance on external NPM packages.


Getting started

1) Install the package

npm install @dbcdk/react-components

2) Import global styles

Important: The component library requires global styles to be imported once in your application.

import '@dbcdk/react-components/themes/dbc.css'

3) Add ThemeScript in your root layout (Next.js example)

The library's shared CSS foundation is still imported normally, but product-theme switching is handled by ThemeScript + useTheme(). ThemeScript restores the persisted color scheme and product theme before paint, so the app avoids a flash of the wrong theme on first load.

import { ReactNode } from 'react'

import { ThemeScript } from '@dbcdk/react-components'
import { TooltipProvider } from '@dbcdk/react-components/client'
import '@dbcdk/react-components/themes/dbc.css'

export default function RootLayout({ children }: Readonly<{ children: ReactNode }>) {
  return (
    <html lang="da" suppressHydrationWarning>
      <head>
        <ThemeScript defaultTheme="dbc" />
      </head>
      <body>
        <TooltipProvider>{children}</TooltipProvider>
      </body>
    </html>
  )
}

4) Switching theme in your application

Example using useTheme() when you want to control the theme:

'use client'

import { AppHeader, Button, useTheme } from '@dbcdk/react-components/client'
import { Moon, Sun } from 'lucide-react'

export default function Header() {
  const { theme, switchTheme } = useTheme({ defaultTheme: 'dbc' })

  return (
    <AppHeader>
      <Button
        variant="outlined"
        onClick={() => switchTheme(theme === 'dbc' ? 'filmstriben' : 'dbc')}
      >
        {theme === 'dbc' ? <Moon /> : <Sun />}
      </Button>
    </AppHeader>
  )
}

Light is the default color scheme, including before hydration. ThemeScript, useColorScheme(), and the color scheme controls preserve saved Dark or System preferences. To default to the OS preference explicitly, use <ThemeScript defaultColorScheme="system" /> and useColorScheme('system').

The hook updates data-product-theme, applies the selected product theme's token overrides, and persists the selection.

If you only need to read the active theme state, use useGetActiveTheme() instead of useTheme():

'use client'

import { useGetActiveTheme } from '@dbcdk/react-components/client'

export function ThemeBadge() {
  const { theme, colorScheme, themeKey } = useGetActiveTheme()

  return <span>{themeKey ?? `${theme} / ${colorScheme}`}</span>
}

useGetActiveTheme() is intentionally read-only. It reports the current active theme state as established by ThemeScript, persisted storage, or another mounted theme control. themeKey is only set when both theme and colorScheme are resolved, and is otherwise null. If your app passes a custom storageKey/colorSchemeStorageKey (see below), pass the same values here so it reads the right persisted state.


5) Running multiple apps on the same origin

useTheme, useColorScheme, useGetActiveTheme, ThemeScript, ThemeButton/ThemeMenuSection, and ColorSchemeButton/ColorSchemeMenuSection all persist to a cookie + localStorage under a shared default key. If two different apps built on this library run on the same origin (e.g. two products under one domain), they'll read and overwrite each other's theme selection by default.

To keep them independent, give each app its own key via storageKey (product theme) and/or colorSchemeStorageKey (color scheme, ThemeScript only — elsewhere it's the storageKey option passed to useColorScheme):

<ThemeScript
  defaultTheme="dbc"
  storageKey="my-app-theme"
  colorSchemeStorageKey="my-app-color-scheme"
/>
const { theme, switchTheme } = useTheme({ defaultTheme: 'dbc', storageKey: 'my-app-theme' })
const { colorScheme, switchColorScheme } = useColorScheme(undefined, {
  storageKey: 'my-app-color-scheme',
})

Use the same keys everywhere in a given app (ThemeScript, the hooks, and any switcher components), or the values will resolve inconsistently.


Package integration

The package targets bundlers (Next.js, Vite, webpack) and test runners with a CSS transform (Vitest, Jest with a CSS Modules mapper). Every entry point - the main entry, /client, /tanstack, /tanstack-virtual and /markdown - imports CSS Modules and ships unbundled files with extensionless relative imports, so Node's own import/require can't load it directly.

Optional table integration

Table and useTableData are available from @dbcdk/react-components/client without TanStack. Pass the same ColumnItem<T>[] to the table and the hook's sorting.columns; the hook uses each column's accessor and optional sortFunction. Use the column id as sortBy.

Existing accessor-shaped columns (accessorKey / accessorFn), including typed TanStack columns, also work with the hook without introducing a TanStack dependency into core declarations. Explicit columnComparators override column-derived sorting.

useTableSettings, TableSettings, and TableSettingsMenuSection are also available from /client, together with their types and localStorageTableSettingsStorage. They accept the normal Table's columns and honor hidden and canHide. Existing TanStack columns with accessorKey, meta.hidden, and enableHiding remain compatible.

Install @tanstack/react-table for TanstackTable from /tanstack. Existing settings exports from /tanstack remain available as compatibility aliases.

Optional Markdown integration

MarkdownEditor and MarkdownViewer are available from @dbcdk/react-components/markdown, not from the main entry or /client. They need markdown-it, which is an optional peer dependency: install it (npm i markdown-it) only if you use them. MarkdownViewer also works in server components; MarkdownEditor is a client component.

Next.js configuration

Add transpilePackages: ['@dbcdk/react-components'] to your Next.js configuration so it processes the library's CSS Modules. Import interactive components and providers from /client. The packed-package integration check covers Next.js 16.2.6 with webpack and React 19.2.4.

Using Storybook

Storybook is the primary documentation and exploration tool for this library.

Local Storybook

npm run storybook

Storybook runs on http://localhost:6006.

In Storybook you can:

  1. Browse components in the left-hand navigation
  2. Open a story to see variants and states
  3. Adjust props via Controls
  4. Read guidelines and usage notes in the Docs tab

Themes

Use the 🎨 theme selector in the Storybook toolbar to switch between available themes (e.g. light and dark).

All components are styled using CSS variables defined in the theme files.


Accessibility (a11y)

Accessibility is a first-class concern in this library.

We aim to ensure that components:

  • Are usable with keyboard navigation
  • Have visible and consistent focus states
  • Work with screen readers
  • Follow common ARIA and semantic HTML best practices

Storybook includes the a11y addon to help identify issues during development.


MIME type helpers

These helpers work on both the server and client and are exported from the root and /client entrypoints:

import { formatMimeType, getMimeTypeExtension } from '@dbcdk/react-components'

formatMimeType('application/pdf') // 'PDF'
formatMimeType('text/plain; charset=utf-8') // 'Tekst'
getMimeTypeExtension('image/jpeg') // 'jpg'

formatMimeType provides Danish labels for common MIME types, preserves unknown values unchanged, and returns - for null or undefined. getMimeTypeExtension returns an extension without a dot for common image types and PDF, or undefined for unsupported types. Both ignore MIME parameters, casing, and surrounding whitespace when looking up known types.


Native selection controls

Use NativeSelect when you want the browser's native <select> interaction, including the platform picker on phones. Select is the custom popover control. Import interactive form controls from the client entrypoint:

'use client'

import { useState } from 'react'
import { NativeSelect } from '@dbcdk/react-components/client'

export function FormatFilter() {
  const [format, setFormat] = useState('')

  return (
    <NativeSelect
      label="Format"
      name="format"
      options={[
        { label: 'Alle formater', value: '' },
        { label: 'PDF', value: 'pdf' },
      ]}
      selectedValue={format}
      onChange={setFormat}
    />
  )
}

NativeSelect includes InputContainer: pass labelAction, helpText, error, orientation, or fullWidth directly rather than adding another label wrapper. It is controlled and single-select: onChange receives the original option value (string, number, or object with datakey), not a DOM event. A provided option with value: '' is a real choice and replaces the synthetic placeholder. Otherwise, selecting the placeholder invokes onClear, when provided.

Native select attributes and the element ref are forwarded, including name, form, required, disabled, and onBlur. Values participate in FormData, and required uses browser validation. allowClearWhenRequired allows clearing but does not bypass that validation. Use MultiSelect for multiple selections.

The default medium text uses the 16px theme token. Coarse pointers get at least the 44px control-height token, while small/medium text uses the 16px token. This keeps mobile controls readable without replacing native interaction. Check the published Storybook on actual iOS Safari and Android Chrome when validating platform picker behavior; desktop emulation does not verify those pickers.


Contributing

See CONTRIBUTING.md for detailed guidelines on:

  • Folder structure
  • Styling and theming rules
  • Storybook requirements
  • TypeScript conventions
  • Versioning and changesets

License

ISC


Group labels, heading sizes, and print content

These options are additive; existing defaults remain unchanged.

import {
  CollapsibleHeadline,
  Headline,
  RadioButtonGroup,
} from '@dbcdk/react-components/client'

// The fieldset's legend stays above the controls. The existing div-based
// orientation/layout remains the default when `as` is omitted.
<RadioButtonGroup
  as="fieldset"
  label="Format"
  direction="row"
  options={[
    { label: 'PDF', value: 'pdf' },
    { label: 'Tekst', value: 'text' },
  ]}
  value={format}
  onChange={setFormat}
  helpText="Vælg ét format."
/>

// An h3 with the theme's 24px/xl type token, without changing heading semantics.
<Headline size={3} textSize="xl">Dokumentoplysninger</Headline>

// Closed on screen initially; includes its content when printing.
<CollapsibleHeadline header="Flere oplysninger" defaultExpanded={false} printExpanded>
  <p>Supplerende indhold.</p>
</CollapsibleHeadline>

Headline.textSize accepts xs, sm, md, lg, xl, and 2xl; omit it to retain the existing responsive heading scale. It also works on CollapsibleHeadline.

printExpanded keeps collapsed children mounted but hidden on screen, including from keyboard navigation and the accessibility tree. Their state and effects remain active. The default still unmounts collapsed children. Printing does not change the expansion state or its persisted value. Any application print flow must still include the component and its stylesheet.

RadioButtonGroup associates its label, error, and help text with the group in both layouts. InputContainer.labelId is also available for custom controls that need to reference their visible label with aria-labelledby.

For FAQ questions that must remain fully readable on narrow screens, use <Accordion wrapHeaders items={items} mode="single" defaultOpenIndex={null} />. Header wrapping is opt-in; existing truncation is unchanged. Closed panels are inert and hidden from assistive technology, including after they have been opened. Their existing animation and retained child state are preserved.

Use Accordion variant="plain" for a lightweight question list with separators between rows and no enclosing border or background. headerWeight optionally sets question weight (400, 500, 600, or 700); the existing default remains 400.

Accordion flushContent removes top and bottom padding from each item's content, including the filled variant's open-panel top spacing. Horizontal content padding and header padding are preserved for every size and variant. Like Tabs, this option defaults to false, keeping existing spacing. Nested Accordions keep their own content spacing, even inside a flush Accordion.

For reusable state policies or persistence, import useAccordionState from the client entrypoint and let it own Accordion's controlled state:

'use client'

import { Accordion, useAccordionState } from '@dbcdk/react-components/client'

const items = [
  { id: 'general', header: 'Generelt', children: <p>Generelle oplysninger</p> },
  { id: 'details', header: 'Detaljer', children: <p>Flere oplysninger</p> },
]

export function Details() {
  const { accordionProps, openAll, closeAll } = useAccordionState({
    itemIds: items.map(item => item.id),
    mode: 'multiple',
    initialOpen: 'all',
    storageKey: 'my-app-details', // Omit for in-memory state only.
  })

  return (
    <>
      <button onClick={openAll}>Åbn alle</button>
      <button onClick={closeAll}>Luk alle</button>
      <Accordion items={items} {...accordionProps} />
    </>
  )
}

itemIds must contain unique, non-empty, stable strings in the same order as items. The hook rejects missing or duplicate IDs rather than falling back to indexes. Accordion itself still supports items without IDs.

initialOpen is a mount-time default: 'none' (the default), 'all', or an array of IDs. In single mode, only the first valid ID is opened; 'all' and openAll() open the first item. closeAll() collapses everything. The result also exposes openIds, setOpenIds(ids), and hydrated.

With storageKey, the hook uses the library's localStorage persistence and restores after mount. Server rendering and the first client render use the same initial state; hydrated signals when restoration is complete. Saved empty arrays intentionally stay collapsed, malformed data falls back to the default, and unavailable storage leaves state usable in memory. Only IDs are saved, so reordering keeps the same sections open; IDs removed from the list are pruned. Use a distinct key for each independent accordion. When switching keys on a mounted hook, the new key is restored before writing or pruning its saved IDs.

Callers can instead supply openIds and onOpenIdsChange for external control; this disables all storage reads and writes. For persistence, let the hook own state and pass its accordionProps to Accordion without overriding them with independent controlled state. Accordion has no storageKey prop, and its existing controlled/uncontrolled props and defaultOpenIndexes="all" remain unchanged.

In MarkdownEditor editOn="click", the Luk button beside Ren tekst ends editing, keeps the current value, calls onEditEnd, and restores focus to the edit button. Its text can be customized with labels.doneButton. The action is also available from preview and plain-text mode; Enter in the textarea keeps its normal newline behavior. On narrow screens, the switch and button occupy their own row.

版本与发布记录

@dbcdk/react-components 共发布 237 个版本,首次发布于 2025/09/04;过去 12 个月发布了 233 个版本。

版本发布日期距今
0.0.240latest2026/11/12今天
0.0.2392026/11/11今天
0.0.2382026/11/11今天
0.0.2372026/11/101 天前
0.0.2362026/11/101 天前
0.0.2352026/11/101 天前

关于 @dbcdk/react-components 的常见问题

@dbcdk/react-components 是什么?

Reusable React components for DBC projects

如何安装 @dbcdk/react-components?

在项目目录运行 npm install @dbcdk/react-components,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^19.0.0)、react-dom(^19.0.0)、lucide-react(>=0.543.0 <2)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@dbcdk/react-components 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dbcdk/react-components 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@dbcdk/react-components 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18.17。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@dbcdk/react-components 可以商用吗?

@dbcdk/react-components 使用 ISC 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@dbcdk/react-components 的下载量有多少?

根据 npm 官方下载统计,@dbcdk/react-components 在 2026/10/12 – 2026/11/10 这 30 天内被下载 7,748 次,日均约 258 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dbcdk/react-components 还在维护吗?

最新版本 0.0.240 发布于 2026/11/12(今天);至今共发布 237 个版本,其中过去 12 个月发布了 233 个;首次发布于 2025/09/04。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。