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
22 changes: 21 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ Versioning follows the policy in [CONTRIBUTING.md](CONTRIBUTING.md#versioning).

## [Unreleased]

## [0.1.0-alpha.2]

### Changed

- The package ships the theming mechanism and one default palette rather than
a catalogue of one product's palettes. `styles/themes/` keeps `orange-light`
and `orange-dark`, the Lablup brand default; the eight `bliss`, `glass`,
`reverie` and `stained` stylesheets are removed. They were byte-identical
copies of files the source product already owns, so nothing is lost: a
product with its own visual identity defines its own `[data-theme]` blocks
over the same 113 token names and ships them itself.
- `styles/base.css` now carries the orange-light values for the 55 tokens a
theme defines, so the default palette and the one shipped theme agree. The
113 token names, the structural values, and every component are unchanged.

Removing a stylesheet path is a breaking change under the versioning policy,
which is why it lands while the package is still an alpha with one consumer
mid-migration.

## [0.1.0-alpha.1]

### Fixed
Expand All @@ -31,6 +50,7 @@ Versioning follows the policy in [CONTRIBUTING.md](CONTRIBUTING.md#versioning).
validation, and a clean external React install fixture.
- Apache-2.0 license and the initial public boundary rules.

[Unreleased]: https://github.com/lablup/ui-common/compare/v0.1.0-alpha.1...HEAD
[Unreleased]: https://github.com/lablup/ui-common/compare/v0.1.0-alpha.2...HEAD
[0.1.0-alpha.2]: https://github.com/lablup/ui-common/compare/v0.1.0-alpha.1...v0.1.0-alpha.2
[0.1.0-alpha.1]: https://github.com/lablup/ui-common/compare/v0.1.0-alpha.0...v0.1.0-alpha.1
[0.1.0-alpha.0]: https://github.com/lablup/ui-common/releases/tag/v0.1.0-alpha.0
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,19 @@ theme:

```ts
// The token contract the components resolve against. Required.
// It also carries the default palette, so this alone is a working theme.
import "@lablup/ui-common/styles/base.css";

// One theme family. Import only the ones you ship.
import "@lablup/ui-common/styles/themes/bliss-light.css";
// Optional, and only if you switch themes at runtime through [data-theme].
import "@lablup/ui-common/styles/themes/orange-dark.css";
```

The package ships the theming mechanism and one default palette, the Lablup
brand orange. A product with its own visual identity defines its own
`[data-theme]` blocks over the same 113 token names and ships them itself,
rather than the package accumulating everyone's palettes. The source product
does exactly that with its five families.

Component CSS travels with the component: importing `Button` brings
`Button.css` with it, so a subpath import pulls that component's styles and no
others. The two entry points above are the only stylesheets you import by hand,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lablup/ui-common",
"version": "0.1.0-alpha.1",
"version": "0.1.0-alpha.2",
"description": "Shared, product-neutral UI components and design tokens for Lablup products",
"license": "Apache-2.0",
"author": "Lablup Inc.",
Expand Down
49 changes: 38 additions & 11 deletions scripts/check-boundary.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,47 @@ const docFiles = globSync(["*.md", "NOTICE", ".github/**/*.yml"], {
absolute: true,
});

/**
* Blank out comment markers without moving anything.
*
* A name that wraps across two comment lines reads as one name and ships as
* one name, but arrives at a line-by-line scan as "Backend.AI" and "GO does",
* neither of which matches. That is not hypothetical: it is how a product
* edition got into this file's own token stylesheet and past this guard.
* Replacing each marker with the same number of spaces lets a pattern span the
* wrap while every offset still maps back to its original line.
*/
function blankCommentMarkers(text) {
return text.replace(/^[ \t]*(?:\/\/+|\/?\*+|#+)[ \t]?/gm, (marker) =>
" ".repeat(marker.length),
);
}

function lineOf(text, index) {
return text.slice(0, index).split("\n").length;
}

for (const file of [...files, ...docFiles]) {
const contents = await readFile(file, "utf8");
contents.split("\n").forEach((line, index) => {
for (const { pattern, reason } of DISCLOSURE) {
if (pattern.test(line)) {
violations.push({
file: relative(root, file),
line: index + 1,
text: line.trim(),
reason: `${reason}; this repository is published publicly`,
});
}
const scannable = blankCommentMarkers(contents);
const lines = contents.split("\n");

for (const { pattern, reason } of DISCLOSURE) {
const global = new RegExp(
pattern.source,
pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`,
);
for (const match of scannable.matchAll(global)) {
const line = lineOf(scannable, match.index);
violations.push({
file: relative(root, file),
line,
// The match rather than the line, since it may span two of them.
text: `${lines[line - 1].trim()} [matched: ${match[0].replace(/\s+/g, " ")}]`,
reason: `${reason}; this repository is published publicly`,
});
}
});
}
}

if (violations.length > 0) {
Expand Down
8 changes: 5 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
* `@lablup/ui-common/components/<Name>` when you want the smallest
* possible graph (see the component subpath exports in package.json).
*
* Styling is opt-in and lives in a separate entry point:
* `@lablup/ui-common/styles/base.css` (required) plus at most one
* `@lablup/ui-common/styles/themes/<theme>.css` (optional).
* Component CSS travels with the component. What stays opt-in is the palette:
* `@lablup/ui-common/styles/base.css` (required, and the default theme on its
* own) plus `styles/themes/orange-dark.css` if you switch through
* `[data-theme]`. A product with its own identity ships its own themes over
* the same token names.
*/

export * from "./components/BaseCard";
Expand Down
90 changes: 44 additions & 46 deletions src/styles/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,36 @@
* reads. This file is the token contract: renaming or removing an entry is
* a breaking change, exactly like renaming a prop (see CONTRIBUTING.md).
*
* Values are the resolved defaults of the source product's default theme
* (theme family "stained", color scheme "light"), taken from that product's
* `src/themes/base.css` (structural tokens: spacing, radius, motion, type
* scale, focus ring, z-index) and `src/themes/tokens/stained-light.css`
* (color and button tokens). A handful of tokens that no source theme
* ever defines (e.g. `--token-colorTextQuaternary`, `--token-marginXXS`)
* are given the same literal fallback value the component CSS already
* carries at its call site.
* Colour and button values are the Lablup brand default, theme family
* "orange", colour scheme "light", which is also the one theme this package
* ships as a stylesheet (`styles/themes/orange-light.css` and its dark
* counterpart). Structural tokens (spacing, radius, motion, type scale, focus
* ring, z-index) are theme-independent and were taken from the source
* product's `src/themes/base.css`. A handful of tokens that no theme ever
* defines (e.g. `--token-colorTextQuaternary`, `--token-marginXXS`) are given
* the same literal fallback value the component CSS already carries at its
* call site.
*
* A product with its own visual identity defines its own `[data-theme]`
* blocks over these 113 names and ships them itself, the way the source
* product does with its five families. This package carries the mechanism and
* one default, not a catalogue of somebody else's palettes.
*
* Every component also gives every token reference here its own inline
* fallback, so a consumer that imports a component without this stylesheet
* still gets a rendered (unthemed) result.
*
* Those inline fallbacks are NOT all equal to the values above. Roughly 143
* of them were inherited verbatim from the source product, where they predate
* current theme and were already dead code (the token is always defined, so
* the fallback never applies). The most visible divergence is
* `--token-colorPrimary`, whose call-site fallback is the older orange
* `#ff7a00` rather than the current `#8b5cf6`.
* Those inline fallbacks are NOT all equal to the values above, and no single
* theme would make them agree: of the 252 references to a theme-defined token,
* the fallback matches orange-light in 135 and stained-light in 168, and
* `--token-colorPrimary` alone is written as `#ff7a00` in some components and
* `#8b5cf6` in others. They were inherited verbatim from the source product,
* where they were already dead code, since the token is always defined and the
* fallback never applies.
*
* Consequence: importing a component WITHOUT this stylesheet renders, but not
* in the documented default palette. Import `styles/base.css`. Aligning the
* fallbacks is a deliberate visual change and was left out of the extraction,
* which was required to preserve current rendering exactly.
* fallbacks is a deliberate visual change and is still out of scope.
*/

:root {
Expand All @@ -55,37 +61,29 @@
--token-buttonGhostBackdrop: none;
--token-buttonGhostBg: transparent;
--token-buttonGhostBgActive: #f5f5f5;
--token-buttonGhostBgHover: rgba(139, 92, 246, 0.06);
--token-buttonGhostBgHover: rgba(255, 122, 0, 0.06);
--token-buttonGhostBorder: 1px solid transparent;
--token-buttonGhostBorderHover: 1px solid transparent;
--token-buttonGhostShadow: none;
--token-buttonGhostShadowHover: none;
--token-buttonPrimaryBackdrop: none;
--token-buttonPrimaryBg:
linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, transparent 50%), #8b5cf6;
--token-buttonPrimaryBgHover:
linear-gradient(180deg, rgba(255, 255, 255, 0.25) 0%, transparent 50%), #7c3aed;
--token-buttonPrimaryBg: var(--token-colorPrimary);
--token-buttonPrimaryBgHover: #e86e00;
--token-buttonPrimaryBorder: none;
--token-buttonPrimaryShadow:
0 2px 6px rgba(139, 92, 246, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.2);
--token-buttonPrimaryShadowHover:
0 4px 10px rgba(139, 92, 246, 0.3), inset 0 1px 1px rgba(255, 255, 255, 0.25);
--token-buttonPrimaryShadow: 0 2px 4px rgba(255, 122, 0, 0.2);
--token-buttonPrimaryShadowHover: 0 4px 8px rgba(255, 122, 0, 0.25);
--token-buttonSecondaryBackdrop: none;
--token-buttonSecondaryBg: rgba(139, 92, 246, 0.04);
--token-buttonSecondaryBg: transparent;
--token-buttonSecondaryBgActive: #f5f5f5;
--token-buttonSecondaryBgHover: rgba(139, 92, 246, 0.08);
--token-buttonSecondaryBorder: 1px solid rgba(139, 92, 246, 0.2);
--token-buttonSecondaryBorderHover: 1px solid rgba(139, 92, 246, 0.3);
--token-buttonSecondaryShadow:
0 1px 3px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.8);
--token-buttonSecondaryBgHover: rgba(255, 122, 0, 0.06);
--token-buttonSecondaryBorder: 1px solid var(--token-colorBorder);
--token-buttonSecondaryBorderHover: 1px solid rgba(255, 122, 0, 0.3);
--token-buttonSecondaryShadow: none;
--token-buttonSuccessBackdrop: none;
--token-buttonSuccessBg:
linear-gradient(180deg, rgba(255, 255, 255, 0.2) 0%, transparent 50%), #007a63;
--token-buttonSuccessBgHover:
linear-gradient(180deg, rgba(255, 255, 255, 0.25) 0%, transparent 50%), #006654;
--token-buttonSuccessBg: var(--token-colorSuccess);
--token-buttonSuccessBgHover: #006654;
--token-buttonSuccessBorder: none;
--token-buttonSuccessShadow:
0 2px 6px rgba(0, 189, 155, 0.25), inset 0 1px 1px rgba(255, 255, 255, 0.2);
--token-buttonSuccessShadow: 0 2px 4px rgba(0, 189, 155, 0.2);
--token-colorBgContainer: #ffffff;
--token-colorBgElevated: #ffffff;
--token-colorBgLayout: #f5f5f5;
Expand All @@ -99,12 +97,12 @@
--token-colorFillTertiary: #f9fafb;
--token-colorError: #c82333;
--token-colorInfo: #0066cc;
--token-colorLink: #8b5cf6;
--token-colorLink: #ff7a00;
--token-colorLinkHover: #7c3aed;
--token-colorPrimary: #8b5cf6;
--token-colorPrimaryActive: #6d28d9;
--token-colorPrimaryBg: rgba(139, 92, 246, 0.1);
--token-colorPrimaryBorder: rgba(139, 92, 246, 0.2);
--token-colorPrimary: #ff7a00;
--token-colorPrimaryActive: #cc6200;
--token-colorPrimaryBg: rgba(255, 122, 0, 0.1);
--token-colorPrimaryBorder: rgba(255, 122, 0, 0.2);
--token-colorSuccess: #007a63;
--token-colorSuccessActive: #005244;
--token-colorSuccessBg: rgba(0, 189, 155, 0.05);
Expand All @@ -116,7 +114,7 @@
--token-colorTextQuaternary: #bfbfbf;
--token-colorTextSecondary: #595959;
--token-colorTextTertiary: #737373;
--token-focusRingColor: #8b5cf6;
--token-focusRingColor: #ff7a00;
--token-focusRingOffset: 2px;
--token-focusRingStyle: solid;
--token-focusRingWidth: 2px;
Expand All @@ -141,9 +139,9 @@
--token-motionDurationSlow: 0.3s;
--token-themeTransitionDuration: 0.25s;
--token-themeTransitionTiming: ease-in-out;
--token-tabActiveBg: rgba(139, 92, 246, 0.1);
--token-tabFocusShadow: 0 0 0 3px rgba(139, 92, 246, 0.15);
--token-tabHoverBg: #f3f4f6;
--token-tabActiveBg: rgba(255, 122, 0, 0.1);
--token-tabFocusShadow: 0 0 0 3px rgba(255, 122, 0, 0.15);
--token-tabHoverBg: var(--token-colorFillSecondary);
--token-zIndexDrawer: 1100;
--token-zIndexDrawerContent: 1101;
--token-zIndexPopover: 1150;
Expand Down
Loading