Sollte eigtl zusammen mit allem in Guidelines zu lesen sein…
Copy link to headline:Contribution Guide
When contributing to onyx, please respect the Schwarz IT Code of Conduct(opens in a new tab) and our technical guidelines.
info Target audience This document is directed at people that are developing for onyx. It gives tips and guidelines on what should or must be considered when working with onyx.
Copy link to headline:Prerequisites / Setup
- Install [Node.js](<<<https://nodejs.org/en)>>>(opens in a new tab);;;; version .
We recommend using fnm(opens in a new tab) for managing your node versions which will automatically use the correct node version when working in the onyx repo.
tip Tip: Let fnm automatically choose the right version
- Run
corepack enableonce - Add
eval $(fnm env --use-on-cd --version-file-strategy=recursive --corepack-enabled --resolve-engines)to your shell startup file (e.g.~/.bash_profile,~/.zshrc)
- Install the pnpm(opens in a new tab) package manager with a compatible version to
^{{ packageManager.replace("pnpm@", "") }}
Copy link to headline:Recommended IDE Setup
We follow the official Vue recommendation for IDE setup which is VSCode(opens in a new tab) with the Vue - Official(opens in a new tab) extension.
Copy link to headline:Global scripts
Depending on which package you want to contribute, there are different scripts available. A full list can be found in the package.json file of the corresponding package.
Here is a list of the most commonly used global scripts that you can run in the monorepo root folder.
Copy link to headline:Create new component
To create and contribute a new component to onyx, follow the steps as described below.
Copy link to headline:Step 1: Get the code
First, you need to get access to the code. Onyx is Open Source and available on GitHub(opens in a new tab).
Just create a fork(opens in a new tab) of the repository to get started.
info Access for employees of the Schwarz group If you are an employee at the Schwarz group(opens in a new tab) you can also get direct access to the onyx GitHub repository so you can create branches in there without needing to fork it.
Just contact one of the team members / engineers.
Copy link to headline:Step 2: Install dependencies
If you haven't already, install the dependencies of the monorepo by running:
Copy link to headline:Step 3: Run Storybook locally
We are using Storybook(opens in a new tab) to develop and document onyx components. The Storybook is also deployed on storybook.onyx.schwarz(opens in a new tab).
To start it locally for development, just run the following command in the monorepo root folder. Afterwards, you can access Storybook on localhost:6006(opens in a new tab)
Copy link to headline:Step 4: Create .vue file
Now, create a new Vue file for your component. All onyx components are located in the packages/sit-onyx/src/components folder. Create a new folder with the name of your component and place a .vue file with your component name in it.
For the rest of this guide, we assume that we want to create a component named OnyxExampleComponent. So you should place it inside packages/sit-onyx/src/component/OnyxExampleComponent/OnyxExampleComponent.vue.
You can/should use the following boilerplate code to start your component:
A few notes on the code snippet above:
- Every onyx component must have the
onyx-componentCSS class set on its root. This is used/needed for normalizing CSS styles, such as remove default margins from HTML elements etc. - The
densityClassmust also be bind to the component root. To learn more about onyx densities, please refer to our density basics. - We use CSS layers to allow users to easily override all styles. You must place all your component styles inside
@include layers.component() { // your styles... } - All component styles are global, so you must use BEM(opens in a new tab) for naming your classes. All nested classes must start with the prefix of your component to avoid global side effects, e.g.
.onyx-example-component__label.
Copy link to headline:Step 5: Create types for properties
To define your component props, create a new types.ts file in the component folder and type the properties as needed for your component.
Note: The type name must start with your component name (see code snippet below) and extend the DensityProp so the density can be set for the component.
Copy link to headline:Step 6: Create stories file
To develop, preview and document the component, you need to create a stories file so the component appear in the Storybook UI.
tip Story title Please make sure to adjust the Story title to use the category that your component fits in. We use "Basic" here but you can see a list of existing categories on storybook.onyx.schwarz(opens in a new tab).
Also make sure to remove the "Onyx" prefix for the title.
Copy link to headline:Step 7: Export component
Last but not least, export the component and its types from the library so other users can actually use/import it once the component is released. Please place the import alphabetically sorted by the component name.
Copy link to headline:Step 8: Create pull request
Once you are done with your changes, create a pull request(opens in a new tab) and describe the changes that you did. Please consider our technical guidelines when contributing to onyx.
If you are contributing multiple components, features or your contributing is big, please split up the contribution and create multiple PRs!
tip Draft PRs Don't worry if your PR is not completely done yet because you have open questions etc. You can also create a draft pull request so the onyx team can help you out or finalize the PR for you.
When your PR gets approved, you can expect a pre-release immediately after it is merged. Production releases are planned to be published every 2 weeks after the release of version 1.0.0.
tip Screenshot tests Component screenshot tests using Playwright will only be performed in our GitHub workflows(opens in a new tab) to ensure consistency of the resulting images which vary on different operating systems.
If you made visual changes to components, you can use this Workflow(opens in a new tab) to update the screenshots on your branch.
Copy link to headline:Step 9: Create changeset
If your changes affect the user and need to be released (e.g. added a new component or feature), you need to add a changeset(opens in a new tab).
With this, the onyx changelog and versioning is managed automatically.
In the monorepo root, run:
and follow the interactive CLI there. Please describe your changes in perspective of the user since this will end up in the changelog.
Copy link to headline:Further reading
Here you can find more details about:
Copy link to headline:Storybook
For development and API documentation we make use of Storybook(opens in a new tab).
A Story is a specific scenario or state of a component that showcases its functionality, appearance, or behavior. It helps developers and designers visualize and test different variations of a component in isolation.
All stories for a single component are kept in a neighboring <component>.stories.ts file.
info
The next sections describe the content of a *.stories.ts file in more detail.
Feel free to skip to the end to see the complete example and a TL;DR.
Copy link to headline:Meta section
First off we have the Meta(opens in a new tab) object.
Here the component is described and documented.
Using tsdoc, a general description is added above the meta constant.
- The
titleattribute defines where in the storybook the component is placed and what its title is. Categories and the title are separated by slashes ("/"). componentsets the Component that will be used for all Stories in this file. The documentation for all properties will be extracted from its types and their tsdoc(opens in a new tab) block.- With the
argTypes(opens in a new tab) attribute the components property documentation can be extended and overwritten. Usually this is not necessary as Storybook infers the properties, their types and description from the component types. However as shown in the example below, it can be useful to overwrite Storybooks input element using thecontrolproperty. - To highlight useful native DOM events, the
withNativeEventLoggingutility can be used. It enables logging and documentation for the defined DOM events.
Make sure to export meta as default so Storybook can find the Story.
<<< ./stories-example.ts#meta
Copy link to headline:Actual Stories
Every non-default export represents a Story, where the export name is also the name of the story.
The first story is usually called Default.
- The description of the story object is done via tsdoc(opens in a new tab).
- We use
StoryObj<typeof OnyxComponent>to enable typed code completion. - A story must define
argswhich represents the props that are passed to component.
To add further stories, add more named exports.
Consider reusing the Default stories args.
<<< ./stories-example.ts#story
Copy link to headline:Slots
Storybook treats Slots like props, therefore they are also configured via the args.
A slot and its content are defined using Render Functions(opens in a new tab).
A Render Functions must return a VNode(opens in a new tab) or an array of VNodes.
<<< ./stories-example.ts#slot
Copy link to headline:TL;DR
tip TL;DR
- Descriptions and types are extracted by Storybook from the components types and tsdoc(opens in a new tab)
- Meta details are configured via a default export with type
Meta<typeof OnyxComponent> - Stories are defined as Named Exports with type
StoryObj<typeof OnyxComponent> - Props and slot content for a Story are passed with the
argsproperty - Slots are defined using Render Functions(opens in a new tab)
Complete example:
<<< ./stories-example.ts
Copy link to headline:Styling
We use SCSS(opens in a new tab) as CSS preprocessor. Any valid CSS is also valid SCSS, that should make it easy to get started.
All our design variables are provided as CSS custom properties(opens in a new tab). An overview can be found here.
For a general overview of our (S)CSS guidelines refer to our CSS Principles.
Copy link to headline:CSS Layers
We make use of Cascade Layers(opens in a new tab) to simplify how different style sources are combined. By putting all our styles into layers they can also be easily overwritten by users.
Therefore, the @include layers.component() mixin must be used.
It will put the contained rules into the onyx.component layer and normalize stylings.
<<< ./styling-example.vue#screenshot-testing {scss}
Copy link to headline:Custom density styles
For most cases, the CSS variables for densities will already support that the component adjusts its spacings based on the current density. In exceptional cases it might be necessary to apply special style rules for the densities, for which the default spacings do not work.
You can use our density mixins in this case:
<<< ./styling-example.vue#densities {scss}
Copy link to headline:Testing
We require every component to be thoroughly tested. onyx uses Playwright(opens in a new tab) and Vitest(opens in a new tab) for testing.
Copy link to headline:Component tests
Generally playwright component tests(opens in a new tab) (kept in .ct.tsx-files) suffice to test a component.
Component tests must include screenshot tests to ensure that any style changes happen intentionally and can be approved by our UX.
To easily generate and test screenshots for all main states the executeMatrixScreenshotTest utility is to be used.
<<< ./testing-example.ct.tsx#executeMatrixScreenshotTest
The utility creates a screenshot for every combination of rows and columns.
These are then combined in into grid and saves a single screenshot of it.
Here is an example for the OnyxButton:

warning Choose the columns and rows carefully, as the number of combination grows quadratically. It might be preferable to create a new screenshot matrix instead of adding more columns/rows.
Accessibility tests (using axe-core(opens in a new tab)) are also run for every combination.
For standalone tests or more complicated setups, toHaveScreenshot(opens in a new tab) can be used directly:
<<< ./testing-example.ct.tsx#toHaveScreenshot
Copy link to headline:Development
In our monorepo component tests are run non-interactively using the pnpm test:playwright script.
To use Playwright interactively run pnpm exec playwright test --ui (add the --headed flag to open the see the- browsers) in the package directory.
Copy link to headline:CI
To investigate failing playwright tests from the CI locally:
You can run pnpm gh:show-report in the root, which will
- Download the `html-report--attempt-x` artifact after the pipeline has finished.
- Unzip the archive
cdinto the package you want to see the report for- Run
pnpm dlx playwright show-report
Copy link to headline:VSCode
We highly recommend to use the Playwright Test for VSCode(opens in a new tab) extension for running component tests in development.
It allows to build and run specific tests interactively, directly from the IDE (see annotation 3 in screenshot beneath).
If you encounter any issues please make sure
- to run the
pnpm buildscript at least once for the package - to only select the playwright config file for the current package your are testing
- to run
Run global teardown,Close All BrowsersandClear Cache
You find the playwright VSCode extension settings (see annotation 2 in screenshot beneath) in the Testing section of VSCode (annotation 1).

Copy link to headline:Unit tests
For self-contained logic, excluding Vue components, unit tests (kept in .spec.ts-files) can be written using Vitest(opens in a new tab).
In our monorepo unit tests are run using the pnpm test script.
Copy link to headline:Patterns
This page explains which common patterns we follow when developing onyx and how to use them. These patterns are implemented through composables(opens in a new tab) and enforced through linting rules(opens in a new tab), where possible.
Copy link to headline:Root Attribute Forwarding
For implementing necessary layout, styling and ARIA requirements, it is often necessary to wrap interactive HTML elements.
To enable the developers to be able to set custom attributes and event-listeners on these, we forward most attributes to the relevant (e.g. input or button) element.
The only attributes that are not forwarded are style and class with the assumption being, that these are only useful and intended to be set on the root element of the component.
<<< ../../../../../packages/sit-onyx/src/utils/attrs.ts#docs
Copy link to headline:(Shared) Child Props Forwarding
When a parent component is a wrapper for another (support) component, the parent usually extends all or a subset of the child's properties.
The relevant child props need then to be forwarded to the child component.
This can easily be achieved by using v-bind(opens in a new tab), e.g.
Unfortunately this has the unwanted side-effect of all extraneous props being applied as attributes. So when the parent defines props which do not exist on the child component, they are treated as fallthrough attributes(opens in a new tab). Besides cluttering the HTML with irrelevant attributes this also can have disruptive side-effects when the prop name accidentally matches a valid html attribute (e.g. inert(opens in a new tab) or hidden(opens in a new tab)).
Which might look like this in the DOM tree:
To avoid this, our useForwardProps composable can be used:
<<< ../../../../../packages/sit-onyx/src/utils/props.ts#docs
Copy link to headline:State Control
We want to give the user maximum control of component state, which is achieved by providing props with two-way binding support via Vue's v-model(opens in a new tab).
To not require the developer to declare refs for state they do not care about, the state will be stored internally if left undefined by the user.
Unfortunately the Vue native defineModel(opens in a new tab) compiler macro behaves not as expected, as it will prefer internal state updates over external, unchanged state (e.g. <Comp :open="true" /> will not be considered anymore after an update:open with value false).
Therefore we created a custom composable useVModel, which will prefer the external state:
<<< ../../../../../packages/sit-onyx/src/composables/useVModel.ts#docs