Build a new component
In both Flutter and web, components are built using the same principles and patterns. We aim for the built components to be close to pixel perfect, accessible and reusable.
- Zeta Flutter
- Zeta Web
Create component file
This file contains the implementation of the component and should follow the structure below:
packages/zeta_flutter/lib/src/components/x/x.dart
/// Description from figma
///
/// Flutter specific description (optional)
///
/// Figma: https://www.figma.com/design/JesXQFLaPJLc1BdBM4sisI/%F0%9F%A6%93-ZDS---Components?node-id=X
///
/// Widgetbook: https://design.zebra.com/flutter/widgetbook/index.html#/?path=components/x/zetax/x
class ZetaX extends ZetaStatelessWidget {
/// The constructor of the component [ZetaX].
const ZetaX({
super.key,
super.rounded,
this.y,
});
/// Description of property y.
final y;
Widget build(BuildContext context) {
...
}
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(...);
}
}
The ZetaStatelessWidget
and ZetaStatefulWidget
classes are lightweight wrappers for StatelessWidget
and StatefulWidget
, adding the rounded
property and default behaviors.
Export the component
packages/zeta_flutter/lib/src/zeta_components.dart
...
export 'src/components/x/x.dart';
...
Only export the main component from the component file (for example, ZetaX
). Avoid exporting internal helper classes, private widgets, or subcomponents that are not intended for public use. Exporting only the main component keeps the public API clean and prevents users from relying on internal implementation details, which may change without notice. This approach also reduces confusion and makes it easier for users to discover and use the intended component.
Create the *book file
This file should use Widgetbook annotations and typically include a single story. The story should demonstrate every variant of the component. If this is not feasible, you may create multiple stories.
widgetbook/lib/src/components/x.widgetbook.dart
.UseCase(
name: 'X',
type: ZetaX,
path: 'componentsPath/X',
designLink: 'https://www.figma.com/design/JesXQFLaPJLc1BdBM4sisI/%F0%9F%A6%93-ZDS---Components?node-id=x',
)
Widget x(BuildContext context) {
return ZetaX(
y: context.knobs.text(label: 'y', initialValue: 'Default value'),
rounded: context.knobs.boolean(label: 'Rounded', initialValue: true),
...
);
}
Before viewing the Widgetbook, you must rebuild it:
cd widgetbook && dart run build_runner build -d
For more information, see the Widgetbook documentation.
Create the example file
This file should provide a straightforward example demonstrating how to use the component. If possible, replicate the default version of the component as shown in Figma. The example app is valuable for debugging during development and for testing the component in a real application context. This example will also be used to showcase the component on design.zebra.com/docs/components/x. Ensure the file follows the structure shown below:
packages/zeta_flutter/lib/src/examples/components/x_example.dart
class XExample extends StatelessWidget {
static const String name = 'X';
const XExample({super.key});
Widget build(BuildContext context) {
return ExampleScaffold(
name: XExample.name,
children: [
// Add your example usage here
],
);
}
}
By default, only the first child in children
is rendered in the documentation. If you need to display more than the first widget, add a key containing the word docs
.
Note: On the documentation website, content is displayed in a container with a height of 360px (width depends on the client viewport). If your component overflows this container, consider adding a scroll view or reducing the amount of content displayed.
Link example page
To add the example page to the example app, add it to the list of components:
example/lib/home.dart
...
final List<Component> components = [
Component(XExample.name, (context) => const XExample()),
...
]
...
Once added to this file, the example app can be used for debugging and demoing.
Create component file
This file contains the implementation of the component and should follow the structure below:
/src/components/x/x.ts
// ...
import styles from "./x.styles.js";
/**
* Description from Figma
*
* Web specific description (optional)
*
* @slot - Description of default (unnamed) slot
* @slot a - Description of slot a
* @cssproperty --b - Description of CSS property b
* @part c - Description of part c
*/
@customElement("zeta-x")
export class ZetaX extends Contourable(LitElement) {
/** Description of property y */
@property({ type: Y, reflect: true }) y;
/** Combine y styles with any super- styles */
static styles = [styles, super.styles || []];
protected render() {
return html`<div part="c">
// Component content goes here
<slot> </slot>
<slot name="a"> </slot>
</div>`;
}
}
declare global {
interface HTMLElementTagNameMap {
"zeta-x": ZetaX;
}
}
We have a number of mixins that can be used to mix in extra functionality
- Contourable - adds rounded (boolean) prop, and styles either the parent or any nodes with class
.contourable-target
- ContourableThree - adds rounded (sharp | rounded | full) prop, and styles either the parent or any nodes with class
.contourable-target
- Flavored - adds flavor prop and respected styles.
- FormField - adds form field logic
- Interactive - adds styles for interactive component, applied either to the parent or any nodes with class
.interactive-target
. - Navigate - adds href to a given attribute
- Popup - makes component popup like a dialog
- Size - adds Size property
You can also create a stylesheet to apply css styles to your component. For this, we are using the lit css function within js:
/src/components/x/x.styles.js
import { css } from "lit";
export default css`
/* CSS Styles go here */
:host {
--b: 4px;
}
`;
Export the component
src/index.ts
// ...
import { ZetaX } from "./components/x/x.js";
// ...
export {
ZetaX,
// ...
};
// ...
Only export the main component from the component file (for example, ZetaX). Avoid exporting internal helper classes, private widgets, or subcomponents that are not intended for public use. Exporting only the main component keeps the public API clean and prevents users from relying on internal implementation details, which may change without notice. This approach also reduces confusion and makes it easier for users to discover and use the intended component.
Create the *book file
We use storybook for web and should typically include a single story. This story should demonstrate every variant of the component. If this is not feasible, you may create multiple stories.
src/stories/components/x/x.stories.ts
...
import { ZetaX } from "../../../components/x/x.js";
import "../../../components/x/x.js";
import { spreadGenerator } from "../../utils.js";
const spread = spreadGenerator(ZetaX);
const meta: Meta<ZetaX> = {
component: "zeta-x",
title: "Components/X",
tags: ["autodocs"],
args: {
// ...
},
argTypes: {
// ...
},
parameters: {
design: {
url: "https://www.figma.com/file/JesXQFLaPJLc1BdBM4sisI/%F0%9F%A6%93-ZDS---Components?x",
},
status: {type: "ready" },
},
};
export default meta;
export const X: StoryObj<X> = {
// Optionally, override the render method
render: args => html`<zeta-x ${spread(args)}>${args.slot}</zeta-x>`
};
Learn more about Storybook.
Create the example file
This file should contain a simple example of how to use the component. If possible, replicate the default version of the component shown in Figma. This example will also be used for demonstrating the component on design.zebra.com/docs/components/x. This file must be structured as shown in the example below:
example/public/components/X.html
<style>
/* Optional styling for the example */
<style>
<zeta-x>
/* ... */
</zeta-x>
Link example page
To add the example page to the example app, add it to the list of components:
example/src/components.json
[
"X"
// ...
]
Test the component
The component should be thoroughly tested before a PR is created. This includes unit tests, widget tests, and integration tests. The tests should cover all the functionality of the component and ensure that it behaves as expected.
For more information on testing, see the Testing Guide.