Revive's Component Library
To work with this project, you'll need to have Node and Yarn installed on your system.
git clone https://github.com/Revive-Home/Aether.git && cd aether
yarn install
Before you start contributing to Aether, be sure to read the contribution guide (TBA)
Aether uses following technologies:
- React
- TypeScript
- Tailwind
- Vite
- Storybook
Run yarn dev
to start Storybook for development.
To style the components, Aether uses TailwindCSS.
The styling of Aether components can be overridden in the consumer apps by passing the Tailwind classes to the className prop.
To avoid the className conflict in consumer apps, Aether's Tailwind is set to require the prefix. All class names should start with the prefix, aether-
.
Example:
<div
className="aether-p-10 aether-text-red-300 aether-border aether-border-red-300"
>
Example
</div>
TODO: Add tailwind guideline/practice
Theme
Aether utilizes the custom theming feature of Tailwind. The custom theming can be configured in tailwind.config.js
.
Aether uses headlessui
to abstract away some of the component behavior logic.
Within a separate application that uses Aether, import the style.css
to apply styling in the root file of the application.
Next.js example:
// in `_app.tsx`
import "@revive-real-estate/aether/dist/style.css"
// ...other imports
And then, import the desired component as follows:
import { Button } from "@revive-real-estate/aether";
Within Aether itself, import the desired component by pointing to the component file. The alias (@
) should be used for the import path:
import { Button } from "@/components/Button";
Aether can be installed and tested locally without publishing to NPM by using yalc.
In Aether, change the version in package.json to indicate this is a test package.
Example: 0.0.0-yalc-test
Then, run
yarn build && yalc publish
yarn build
will build the package and create /dist
, then yalc publish
command will copy all the files that should be published in remote NPM registry.
Then in consuming app, run
yalc add <package_name@version_number> && yarn
The <@version_number>
should match the version in package.json of Aether.
To import Aether components, refer to the Usage section.
All components should be written in TypeScript. Export the interface or type so they can be consumable in the app.
Each prop defined in the interfaces or types should have a description. Storybook Docs will translate these descriptions and generates the Props Table.
Example:
export interface ButtonProps
extends React.HTMLAttributes<HTMLButtonElement | HTMLAnchorElement> {
/** Text within the button */
children?: React.ReactNode | React.ReactNode[];
/** Provide a custom className that is applied directly to the button */
className?: string;
/** Specify whether the control is disabled */
disabled?: boolean;
/** Specify the theme of the button */
variant?: "primary" | "secondary" | "gray" | "turquoise";
}
The components need to be exported in the src/index.ts
file to be consumable. The type definition will be automatically generated by the Vite and included to the bundle.
To make it easier and quicker for developers to debug, it is recommended that the className
of a component begins with a description of the component itself, such as className="aether-button"
.
If there are multiple classNames that get added conditionally, clsx
should be used to consolidate all className
s into a string. This helps in avoiding any potential conflicts or errors that may occur due to multiple classNames being added separately.
const classes = clsx(
"aether-button",
ButtonClasses.baseStyle,
ButtonClasses[size],
variant && ButtonClasses[variant],
disabled && ButtonClasses.disabled,
fullWidth && ButtonClasses.fullWidth,
loading && ButtonClasses.loading,
className // className prop should be placed at the end
);
Many of Aether components support {...rest} props, allowing you to pass additional attributes directly to the HTML elements rendered by the component. This feature provides flexibility for extending our built-in components in various ways.
- Custom Styling: Add custom classes or styles directly to the component.
- Event Handling: Attach event listeners like onClick, onBlur, etc.
- Accessibility: Add attributes like aria-label, aria-labelledby, etc., to improve accessibility.
- Forward Compatibility: Components will automatically support new HTML and React attributes as they are introduced.
// Example: Adding custom styles to a Button component using `style` attribute
import { Button } from "@revive-real-estate/aether";
<Button style={{ backgroundColor: 'tomato' }} onClick={handleClick}>
Click Me
</Button>
There's a set of template files for a component. Copy the src/example/ComponentTemplate
folder and paste it inside the src/components,
then rename the folder and files to an appropriate component name.
Aether supports the import/export of SVG files. The custom icons should be created by the design team and exported as an SVG file.
Steps to add SVG icon to Aether:
- Export the SVG files for the desired icon from Figma
- Add SVG files to
src/components/Icon/aether-icon-files
- In
src/components/Icon/aether-icon-files/index.tsx
, add an export of the newly added icon:
//...more icons
import { ReactComponent as NewIconGreen } from "./green/new-icon.svg"
export {
// ...more icons
NewIconGreen
}
- In
src/components/Icon/data.ts
, add the icon name.
Example:
export const AetherIconNamesArray = [
// ...more icons
"new-icon",
] as const
- In
src/components/Icon/aether-icons.ts
, import the newly added icon fromsrc/components/Icon//aether-icon-files
:
import {
// ...more icons
NewIconGreen
} from "./aether-icon-files"
- Add new entry to iconMap object in
src/components/Icon/aether-icons.ts
export const getAetherIcon = (name: ListIconNames): SvgIcon | undefined => {
const iconMap: IconMap = {
// ...more icons
"new-icon-green": NewIconGreen
}
// ...more code
}
For unit testing, Aether uses Vitest and React Testing Library.
We use Chromatic, integrated with GitHub Actions, to automatically detect visual regressions in our UI components. This tool compares new changes against established baselines, helping us maintain visual consistency. Our main branch Chromatic link serves as a reference for visual comparisons. This link below gets updated to reflect the latest main
branch.
Chromatic link: https://main--63cad365fab53dc41982eaae.chromatic.com/
Our package publishing process is fully automated using GitHub Actions and semantic-release
. This setup ensures a consistent and efficient release process without manual intervention.
Whenever a push is made to the main
branch, the GitHub Actions workflow is triggered. It sets up the required environment, builds the package, and utilizes semantic-release
to handle versioning and changelog generation. The new version is then automatically published to NPM, and a corresponding GitHub release is created.
This streamlined approach allows us to focus on development, knowing that our releases are handled with precision and transparency. Configuration details can be found in the .releaserc file and the corresponding GitHub Actions workflow file in our repository.