Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .agents/skills/bump/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ description: 'Bump package versions and update CHANGELOG.md so consumers can see
## Workflow

1. **Identify affected packages**: `git diff origin/main...HEAD --stat` and `git log origin/main..HEAD` show which packages changed. Ignore packages with no consumer-facing change.
2. **Bump versions**: increment the patch number (`0.0.x` + 1) in each affected `<package>/package.json`. The repository convention is one bump per feature branch, folded into the feature commit.
3. **Align internal peer dependencies**: if a package now relies on something introduced in another package of the same branch (e.g. `react` consuming new `styles` CSS classes), update the corresponding `peerDependencies` range (e.g. `"@ippon-ui/styles": "~0.0.7"`).
2. **Bump versions**: in each affected `<package>/package.json`, increment the patch number (`0.0.x` + 1), or the minor number (`0.x.0`) if the branch contains a breaking change (see [Breaking changes](#breaking-changes)). The repository convention is one bump per feature branch, folded into the feature commit.
3. **Align internal peer dependencies**: if a package now relies on something introduced in another package of the same branch (e.g. `react` consuming new `styles` CSS classes), update the corresponding `peerDependencies` range (e.g. `"@ippon-ui/styles": "~0.0.7"`). Moving that range to a breaking version of the other package is itself breaking for the consumers of this package, so bump this package's minor number too.
4. **Reinstall**: run `mise setup` so the lockfile stays consistent.
5. **Update `CHANGELOG.md`**: add a release entry right after the introduction, above the previous entries.
6. **Verify**: `mise build`, `mise lint-ci` and `mise test-unit-ci` must pass.
Expand All @@ -34,6 +34,21 @@ description: 'Bump package versions and update CHANGELOG.md so consumers can see
- Use the [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) categories: `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`. Only include the categories that apply.
- One line per change, in English, consumer-focused: describe what the consumer gets or must adapt, not internal details (CI, tests, tooling stay out unless they affect consumers).
- Wrap component, class, prop and token names in backticks.
- Start a breaking line with `- **Breaking** — ` and give the migration: what to replace with what, and what visibly breaks if the consumer does nothing.

## Breaking changes

The packages are pre-1.0, where a patch bump is not enough to protect consumers: `~0.0.11` means `>=0.0.11 <0.1.0`, so a consumer on `~0.0.10` installs `0.0.12` automatically and inherits the break with no signal. Only a minor bump falls outside their range. **A branch containing a breaking change bumps the minor number, never the patch number.**

A change is breaking as soon as an existing consumer must edit something to keep the current behaviour. In this repository that mostly means:

- `styles`: a CSS class renamed, removed, or now required somewhere it was not (hand-written HTML has no compiler to catch it), a Pug mixin option renamed or removed, a design token removed or renamed.
- `react`: a component or a prop renamed or removed, a prop becoming required, a rendered structure that consumers select from their own CSS or tests.
- `icons`: an icon name removed or renamed.

Recomposing a component out of a new atom is a typical breaking case: the React component keeps working, but the markup it documents changed, so hand-written HTML must follow. Look for it in `git diff origin/main...HEAD` on the `.pug` and `.scss` files, not only in the public API.

When in doubt, ask what a consumer already on the previous version has to do after upgrading. If the answer is not "nothing", it is breaking.

## Conventions

Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,23 @@ All notable changes to the Ippon UI packages are documented in this file, so con

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), with one entry per release listing the affected package versions.

## 2026-07-29 — @ippon-ui/styles 0.1.0 · @ippon-ui/react 0.1.0

### Added

- `error-area` molecule: fills a zone whose content could not be loaded, with a title, a description and no frame of its own so it sits inside the card or grid cell that already draws one. An action (a retry button) and a detail are both optional; the detail hides a stack trace or an error payload behind a native `details` disclosure. Its padding grows once the surrounding `container` is wide enough.
- `IpponErrorArea` React component: `title` and `description` props, children rendered as the action slot, and an optional `detailMessage` accepting a `string` or an `Error` (rendering its `stack`, falling back to its `message`) together with the required `detailLabel` and an optional `language`.
- `icon-surface` atom: an icon laid on a rounded surface, with `-success`, `-error`, `-information` and `-warning` alternatives on a neutral default.
- `IpponIconSurface` React component with a `color` prop.
- `code` atom: a preformatted code block carrying the frame and the monospace typography, scrolling instead of stretching its container. Giving a language adds the `language-*` class [Prism](https://prismjs.com) styles and reads. Its documentation lists the stylesheet and scripts a page must load for Prism to colorize anything, and what the theme takes over on the typography.
- `IpponCode` React component with `language` and `className` props.
- `code` typography tokens (`--ippon-typography-code-font`, `-size`, `-line-height`, `-weight`).

### Changed

- `prism-ippon.css` uses the new `--ippon-typography-code-font` token instead of a hard-coded font stack. That token is currently `monospace` alone, so highlighted code now renders with the browser default monospace font rather than the previous `Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono'` stack.
- **Breaking** — `import-file` molecule renders its icon through the new `icon-surface` atom, which changes the markup it expects. Hand-written HTML must replace `ippon-import-file--icon` with `ippon-icon-surface` on the icon wrapper, and add `ippon-import-file---icon-surface` next to `ippon-import-file` on the root label. Without the first change the round surface behind the icon disappears; without the second it stops turning blue on hover and drag over. `IpponImportFile` consumers have nothing to do, unless they select `.ippon-import-file--icon` from their own CSS or tests.

## 2026-07-24 — @ippon-ui/styles 0.0.11

### Added
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 7 additions & 5 deletions react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@ippon-ui/react",
"description": "Ippon UI React Component Library",
"version": "0.0.9",
"version": "0.1.0",
"license": "Apache-2.0",
"repository": {
"type": "git",
Expand Down Expand Up @@ -39,15 +39,12 @@
},
"peerDependencies": {
"@ippon-ui/icons": "~0.0.2",
"@ippon-ui/styles": "~0.0.10",
"@ippon-ui/styles": "~0.1.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@babel/core": "^7.29.0",
"vite-plugin-dts": "^5.0.1",
"@eslint/js": "^10.0.1",
"@ippon-ui/icons": "workspace:*",
"@ippon-ui/styles": "workspace:*",
Expand All @@ -58,6 +55,7 @@
"@testing-library/react": "^16.3.2",
"@types/babel__core": "^7.20.5",
"@types/node": "^24.12.3",
"@types/prismjs": "^1.26.6",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
Expand All @@ -69,10 +67,14 @@
"eslint-plugin-storybook": "^10.4.4",
"globals": "^17.6.0",
"jsdom": "^29.1.1",
"prismjs": "1.30.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"storybook": "^10.4.4",
"typescript": "~6.0.2",
"typescript-eslint": "^8.59.2",
"vite": "^8.0.12",
"vite-plugin-dts": "^5.0.1",
"vitest": "4.1.6"
}
}
1 change: 1 addition & 0 deletions react/src/IconSurface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type IpponIconSurfaceColor = 'success' | 'error' | 'information' | 'warning';
20 changes: 20 additions & 0 deletions react/src/IpponCode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { clsx } from 'clsx';
import type { DataSelectableWithChildren } from './DataSelectable.ts';

type IpponCodeProps = DataSelectableWithChildren<{
language?: string;
className?: string;
}>;

export const IpponCode = (props: IpponCodeProps) => {
const languageClass = props.language ? `language-${props.language}` : undefined;

return (
<pre
className={clsx('ippon-code', props.className, languageClass)}
data-selector={props.dataSelector}
>
<code>{props.children}</code>
</pre>
);
};
71 changes: 71 additions & 0 deletions react/src/IpponErrorArea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { DataSelectableWithChildren } from './DataSelectable.ts';
import { IpponCode } from './IpponCode.tsx';
import { IpponIcon } from './IpponIcon.tsx';
import { IpponIconSurface } from './IpponIconSurface.tsx';
import { IpponIon } from './IpponIon.tsx';
import { IpponText } from './IpponText.tsx';
import { IpponVSpace } from './IpponVSpace.tsx';

type IpponErrorAreaCommonProps = {
title: string;
description: string;
};

type IpponErrorAreaWithoutDetailProps = IpponErrorAreaCommonProps & {
detailMessage?: never;
detailLabel?: never;
language?: never;
};

type IpponErrorAreaWithDetailProps = IpponErrorAreaCommonProps & {
detailMessage: string | Error;
detailLabel: string;
language?: string;
};

type IpponErrorAreaVanillaProps = IpponErrorAreaWithoutDetailProps | IpponErrorAreaWithDetailProps;

type IpponErrorAreaProps = DataSelectableWithChildren<IpponErrorAreaVanillaProps>;

const toDetailContent = (detailMessage: string | Error): string => {
if (detailMessage instanceof Error) {
return detailMessage.stack ?? detailMessage.message;
}
return detailMessage;
};

export const IpponErrorArea = (props: IpponErrorAreaProps) => (
<div className="ippon-error-area" data-selector={props.dataSelector}>
<IpponVSpace gap={12} align="center">
<IpponIconSurface color="error">
<IpponIcon name="warning" variant="outline" size={24} />
</IpponIconSurface>
<IpponVSpace gap={4} align="center">
<IpponText variant="body" weight="bold">
{props.title}
</IpponText>
<IpponText variant="body" size="small" color="neutral-tertiary-inversed">
{props.description}
</IpponText>
</IpponVSpace>
{props.detailMessage && (
<details className="ippon-error-area--detail">
<summary className="ippon-error-area--summary">
<IpponIon name="chevron-forward" className="ippon-error-area--chevron" />
<IpponText variant="body" size="small" weight="bold">
{props.detailLabel}
</IpponText>
</summary>
<IpponCode
className="ippon-error-area--code"
language={props.language}
dataSelector={props.dataSelector ? `${props.dataSelector}.detail` : undefined}
>
{toDetailContent(props.detailMessage)}
</IpponCode>
</details>
)}
{props.children}
</IpponVSpace>
</div>
);
17 changes: 17 additions & 0 deletions react/src/IpponIconSurface.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { clsx } from 'clsx';
import type { DataSelectableWithChildren } from './DataSelectable.ts';
import { optionalToAlternativeClass } from './CAP.ts';
import type { IpponIconSurfaceColor } from './IconSurface.ts';

type IpponIconSurfaceProps = DataSelectableWithChildren<{
color?: IpponIconSurfaceColor;
}>;

export const IpponIconSurface = (props: IpponIconSurfaceProps) => (
<div
className={clsx('ippon-icon-surface', optionalToAlternativeClass(props.color))}
data-selector={props.dataSelector}
>
{props.children}
</div>
);
7 changes: 4 additions & 3 deletions react/src/IpponImportFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useRef, useState } from 'react';
import { Optional } from './Optional.ts';
import type { DataSelectable } from './DataSelectable.ts';
import { IpponIcon } from './IpponIcon.tsx';
import { IpponIconSurface } from './IpponIconSurface.tsx';
import { IpponText } from './IpponText.tsx';
import { IpponVSpace } from './IpponVSpace.tsx';
import { toAlternativeClass } from './CAP.ts';
Expand Down Expand Up @@ -92,7 +93,7 @@ export const IpponImportFile = (props: IpponImportFileProps) => {

return (
<label
className={clsx('ippon-import-file', {
className={clsx('ippon-import-file', 'ippon-import-file---icon-surface', {
[toAlternativeClass('dragover')]: isDragover,
})}
data-selector={props.dataSelector}
Expand All @@ -102,9 +103,9 @@ export const IpponImportFile = (props: IpponImportFileProps) => {
onDrop={handleDrop}
>
<IpponVSpace gap={8} align="center">
<div className="ippon-import-file--icon">
<IpponIconSurface>
<IpponIcon name="cloud-upload" size={24} />
</div>
</IpponIconSurface>
<IpponVSpace gap={4} align="center">
<IpponText variant="body" weight="bold">
{props.title}
Expand Down
3 changes: 3 additions & 0 deletions react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ export { IpponBadge } from './IpponBadge.tsx';
export { IpponButton } from './IpponButton.tsx';
export { IpponButtonCard } from './IpponButtonCard.tsx';
export { IpponCard } from './IpponCard.tsx';
export { IpponCode } from './IpponCode.tsx';
export { IpponContainer } from './IpponContainer.tsx';
export { IpponDropdown } from './IpponDropdown.tsx';
export { IpponErrorArea } from './IpponErrorArea.tsx';
export { IpponField } from './IpponField.tsx';
export { IpponGrid, IpponGridSlot } from './IpponGrid.tsx';
export { IpponHelperText } from './IpponHelperText.tsx';
export { IpponHSpace, IpponHSpaceSlot } from './IpponHSpace.tsx';
export { IpponIcon } from './IpponIcon.tsx';
export { IpponIconSurface } from './IpponIconSurface.tsx';
export { IpponImportFile } from './IpponImportFile.tsx';
export { IpponInputText } from './IpponInputText.tsx';
export { IpponIon } from './IpponIon.tsx';
Expand Down
Loading
Loading