Skip to content

Commit

Permalink
color
Browse files Browse the repository at this point in the history
  • Loading branch information
NgocNhi123 committed Jul 9, 2024
1 parent bb21bbf commit 5790a7e
Show file tree
Hide file tree
Showing 22 changed files with 683 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/docs/patterns/color/background/background.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Canvas, Meta, Story } from "@storybook/blocks";
import { ColorBackground } from "./background";

<Meta title="Patterns/Color/Background" />

# Background Color

The `background` utility contains classes that set the
[CSS `background-color`][1] property. This is the recommended way to set
background color since they automatically change based on the current theme.

```ts
import { background } from "@moai/core";

<div className={background.weak}>Text</div>;
```

There are only 2 colors in the `background` utility at the moment:

<ColorBackground rows={[{ key: "weak" }, { key: "strong" }]} />

The `weak` value sets a light gray background on light theme and a daker
background on dark theme. It should be used for underlying backgrounds, like
the background of your app. It can also be used to separate an area, such as
the header of a table.

The `strong` value sets a white background on light theme and a lighter
background on dark theme. It should be used for elevated containers, such as
panes, toolbars or popovers.

## See also

- The [Pane][2] component uses `strong` background along with border and shadow
to better elevate contents.

[1]: https://developer.mozilla.org/en-US/docs/Web/CSS/background-color
[2]: /docs/components-pane-docs
11 changes: 11 additions & 0 deletions src/docs/patterns/color/background/background.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.name {
font-family: "Source Code Pro", monospace;
width: 160px;
min-width: 160px;
}

.container {
overflow: auto;
white-space: nowrap;
border-width: 2px;
}
48 changes: 48 additions & 0 deletions src/docs/patterns/color/background/background.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { border, background, Table, text } from "../../../../core";
import { ColorSample } from "../sample/sample";
import s from "./background.module.css";

type BackgroundKey = keyof typeof background;

interface Row {
key: BackgroundKey;
}

const MakeColumn =
(theme: "light" | "dark", text: string) =>
(row: Row): JSX.Element => (
<div className={theme}>
<ColorSample
background={background[row.key]}
foreground={{ type: "text", cls: text, usage: "both" }}
/>
</div>
);

const LightStrong = MakeColumn("light", text.normal);
const LightWeak = MakeColumn("light", text.muted);
const DarkStrong = MakeColumn("dark", text.normal);
const DarkWeak = MakeColumn("dark", text.muted);

interface Props {
rows: Row[];
}

export const ColorBackground = (props: Props): JSX.Element => (
<div className={[s.container, border.weak].join(" ")}>
<Table<Row>
size={Table.sizes.small}
fixed={{ firstColumn: true }}
fill
rows={props.rows}
rowKey={(row) => row.key}
columns={[
{ title: "Name", className: s.name, render: "key" },
{ title: "Light", render: LightStrong },
{ title: "Light (muted)", render: LightWeak },
{ title: "Dark", render: DarkStrong },
{ title: "Dark (muted)", render: DarkWeak },
]}
/>
</div>
);
28 changes: 28 additions & 0 deletions src/docs/patterns/color/border/border.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Meta, Canvas, Story } from "@storybook/blocks";
import { ColorBorder } from "./border";

<Meta title="Patterns/Color/Border" />

# Border Color

The `border` utility contains classes that set the [CSS `border-color`][1]
property. This is the recommended way to set border color since they
automatically change based on the current theme.

Note that this only set the color. To have a border, you also need to set a
width, e.g. using the "border" class from Tailwind. You don't need to set the
border style since it's already default to "solid" in our [CSS reset][2].

```ts
import { border } from "@moai/core";

// The "border" class comes from Tailwind to set the border width
<div className={[border.weak, "border"].join(" ")}>Text</div>;
```

There are 2 colors in the `border` utility at the moment:

<ColorBorder rows={[{ key: "weak" }, { key: "strong" }]} />

[1]: https://developer.mozilla.org/en-US/docs/Web/CSS/border-color
[2]: https://github.com/moaijs/moai/blob/739a87de82bd061bb41f38c5a51a410b59944a3d/lib/core/src/style/reset.css#L404-L417
11 changes: 11 additions & 0 deletions src/docs/patterns/color/border/border.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.name {
font-family: "Source Code Pro", monospace;
width: 160px;
min-width: 160px;
}

.container {
overflow: auto;
white-space: nowrap;
border-width: 2px;
}
48 changes: 48 additions & 0 deletions src/docs/patterns/color/border/border.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { background, border, Table } from "../../../../core";
import { ColorSample } from "../sample/sample";
import s from "./border.module.css";

type BorderKey = keyof typeof border;

interface Row {
key: BorderKey;
}

const MakeColumn =
(theme: "light" | "dark", back: string) =>
(row: Row): JSX.Element => (
<div className={theme}>
<ColorSample
background={back}
foreground={{ type: "border", cls: border[row.key] }}
/>
</div>
);

const LightStrong = MakeColumn("light", background.strong);
const LightWeak = MakeColumn("light", background.weak);
const DarkStrong = MakeColumn("dark", background.strong);
const DarkWeak = MakeColumn("dark", background.weak);

interface Props {
rows: Row[];
}

export const ColorBorder = (props: Props): JSX.Element => (
<div className={s.container}>
<Table<Row>
size={Table.sizes.small}
fixed={{ firstColumn: true }}
fill
rows={props.rows}
rowKey={(row) => row.key}
columns={[
{ title: "Name", className: s.name, render: "key" },
{ title: "Light", render: LightStrong },
{ title: "Light (alt bg)", render: LightWeak },
{ title: "Dark", render: DarkStrong },
{ title: "Dark (alt bg)", render: DarkWeak },
]}
/>
</div>
);
23 changes: 23 additions & 0 deletions src/docs/patterns/color/category/category.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Meta, Canvas, Story } from "@storybook/blocks";
import { ColorCategoryTable } from "./category";

<Meta title="Patterns/Color/Category" />

# Category Colors

Category colors have no semantic meaning attached. Instead, they are used to
show relationships between elements (e.g. categorization, labelling in data
visualizations). They still conform to the AA level of [WCAG of contrast
ratios][1].

## Usage

[At the moment][2], Moai does not support using category colors directly.
Instead, category colors are used via several components that support them:

<ColorCategoryTable />

(The list is quite short at the moment. We are adding more to it!)

[1]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast
[2]: https://github.com/moaijs/moai/issues/210
3 changes: 3 additions & 0 deletions src/docs/patterns/color/category/category.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.container {
border-width: 2px;
}
37 changes: 37 additions & 0 deletions src/docs/patterns/color/category/category.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";
import { border, Table } from "../../../../core";
import { GalleryTag } from "../../../../gallery";
import s from "./category.module.css";

interface Row {
name: string;
link: string;
example: React.ReactNode;
}

const Name = (row: Row): JSX.Element => (
<a href={row.link} children={row.name} />
);

const Example = (row: Row): JSX.Element => <div children={row.example} />;

export const ColorCategoryTable = (): JSX.Element => (
<div className={[s.container, border.weak].join(" ")}>
<Table<Row>
rows={[
{
name: "Tag",
link: "/docs/components-tag",
example: <GalleryTag />,
},
]}
rowKey={(row) => row.name}
columns={[
{ title: "Component", render: Name },
{ title: "Example", render: Example },
]}
fill
fixed={{ firstColumn: true }}
/>
</div>
);
20 changes: 20 additions & 0 deletions src/docs/patterns/color/color.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Canvas, Meta, Story } from "@storybook/blocks";

<Meta title="Patterns/Color/_Index" />

# Colors (index page)

For recommended color usages, see [Text][1], [Background][2], and [Border][3].
They provide accessible colors that are automatically changed based on the
current theme.

For colors that are persistent across themes, see [Static Colors][4].

For colors that have no semantic meaning and should be used for data
visualizations, see [Category Colors][5]

[1]: /docs/patterns-color-text--docs
[2]: /docs/patterns-color-background--docs
[3]: /docs/patterns-color-border--docs
[4]: /docs/patterns-color-static--docs
[5]: /docs/patterns-color-category--docs
17 changes: 17 additions & 0 deletions src/docs/patterns/color/sample/sample.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.container {
padding: 12px;
width: 120px;
font-variant-numeric: "tabular-nums";
border-radius: 4px;

display: flex;
justify-content: space-between;
align-items: center;
}

.border {
width: 16px;
height: 16px;
border-radius: 16px;
border-width: 1px;
}
82 changes: 82 additions & 0 deletions src/docs/patterns/color/sample/sample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Color from "color";
import { useEffect, useRef, useState } from "react";
import { HiCheckCircle } from "react-icons/hi";
import { CategoryColor, Icon, Tag, categoryColors } from "../../../../core";
import s from "./sample.module.css";

export type ColorSampleUsage = "text" | "icon" | "both";

interface Props {
background: string;
foreground:
| { type: "text"; cls: string; usage: ColorSampleUsage }
| { type: "border"; cls: string };
}

const getColor = (contrast: number): CategoryColor => {
const rounded = Math.round(contrast * 10) / 10;
if (rounded >= 4.5) return categoryColors.green;
if (rounded >= 3) return categoryColors.yellow;
return categoryColors.red;
};

const getContrast = (
props: Props,
backElement: HTMLDivElement,
foreElement: HTMLElement,
): number => {
const back = window.getComputedStyle(backElement).backgroundColor;
const foreStyle = window.getComputedStyle(foreElement);
const isText = props.foreground.type === "text";
// Only use long hand name.
// See: https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle#notes
const fore = foreStyle[isText ? "color" : "borderLeftColor"];
return Color(back).contrast(Color(fore));
};

const ColorIcon = (): JSX.Element => (
<Icon component={HiCheckCircle} size={16} display="inline" />
);

export const ColorSample = (props: Props): JSX.Element => {
const backRef = useRef<HTMLDivElement>(null);
const foreRef = useRef<HTMLElement>(null);
const [contrast, setContrast] = useState(0);

useEffect(() => {
window.setTimeout(() => {
const [back, fore] = [backRef.current, foreRef.current];
if (back === null) throw Error("backElm is null");
if (fore === null) throw Error("foreElm is null");
setContrast(getContrast(props, back, fore));
}, 0); // Wait for all styles are applied
}, [setContrast, props]);

return (
<div
ref={backRef}
className={[props.background, s.container].join(" ")}
>
{/* "background" also set color so the "fore" must be in another
element of the "back" */}
{props.foreground.type === "text" ? (
<span ref={foreRef} className={props.foreground.cls}>
{props.foreground.usage !== "icon" && <span>Aa</span>}
{props.foreground.usage === "both" && <span> </span>}
{props.foreground.usage !== "text" && <ColorIcon />}
</span>
) : (
<span
ref={foreRef}
className={[s.border, props.foreground.cls].join(" ")}
/>
)}
<span title="Color contrast" aria-label="Color contrast">
<Tag
color={getColor(contrast)}
children={contrast.toFixed(1)}
/>
</span>
</div>
);
};
6 changes: 6 additions & 0 deletions src/docs/patterns/color/static/grid.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.container {
display: grid;
grid-template-columns: repeat(auto-fit, 300px);
gap: 16px;
max-width: 640px;
}
11 changes: 11 additions & 0 deletions src/docs/patterns/color/static/grid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import s from "./grid.module.css";
import { ColorStaticTable } from "./table";

export const ColorStaticGrid = (): JSX.Element => (
<div className={s.container}>
<ColorStaticTable name="gray" />
<ColorStaticTable name="highlight" />
<ColorStaticTable name="success" />
<ColorStaticTable name="failure" />
</div>
);
Loading

0 comments on commit 5790a7e

Please sign in to comment.