Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Group formats #137

Merged
merged 20 commits into from
Dec 19, 2023
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
57 changes: 57 additions & 0 deletions src/lib/components/colors/FormatGroup.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<script lang="ts">
import { inGamut } from 'colorjs.io/fn';
import type { PlainColorObject } from 'colorjs.io/types/src/color';

import Output from '$lib/components/colors/Output.svelte';
import type { FormatGroup } from '$lib/constants';
import { ColorSpace } from '$lib/stores';
import { getSpaceFromFormatId } from '$lib/utils';

import ExternalLink from '../util/ExternalLink.svelte';

export let type: 'bg' | 'fg';
export let color: PlainColorObject;
export let formatGroup: FormatGroup;

function inGamutForSpace(color: PlainColorObject) {
if (!formatGroup.gamutFormat) return true;
const gamutSpace = ColorSpace.get(
getSpaceFromFormatId(formatGroup.gamutFormat),
);
return inGamut(color, gamutSpace);
}
$: isInGamut = inGamutForSpace(color);
</script>

<div data-content="format-group">
<div class="format-group-heading">
<h2 class="label section-heading">{formatGroup.name}</h2>
{#if !isInGamut}
<span data-color-info="warning"
>Selected color is <ExternalLink
href="https://www.w3.org/TR/css-color-4/#out-of-gamut"
>outside the {formatGroup.gamutName} gamut.</ExternalLink
></span
>
{/if}
</div>
{#each formatGroup.formats as format (format)}
<Output {type} {color} {format} />
{/each}
</div>

<style lang="scss">
[data-content~='format-group'] {
--heading-transform: none;

margin-block-end: var(--double-gutter);
}

.format-group-heading {
align-items: baseline;
display: flex;
flex-wrap: wrap;
gap: var(--shim);
margin-block-end: var(--gutter);
}
</style>
36 changes: 26 additions & 10 deletions src/lib/components/colors/Formats.svelte
Original file line number Diff line number Diff line change
@@ -1,31 +1,47 @@
<script lang="ts">
import type { PlainColorObject } from 'colorjs.io/types/src/color';

import Output from '$lib/components/colors/Output.svelte';
import type { ColorFormatId } from '$lib/constants';
import { FORMATS } from '$lib/constants';
import FormatGroup from '$lib/components/colors/FormatGroup.svelte';
import type {
ColorFormatId,
FormatGroup as FormatGroupType,
} from '$lib/constants';
import { FORMAT_GROUPS } from '$lib/constants';

export let type: 'bg' | 'fg';
export let color: PlainColorObject;
export let format: ColorFormatId;

function otherFormatGroups(
selectedFormat: ColorFormatId,
): typeof FORMAT_GROUPS {
const otherFormats: FormatGroupType[] = [];
FORMAT_GROUPS.forEach((group) => {
const groupFormats = group.formats.filter((s) => s !== selectedFormat);
if (groupFormats.length) {
otherFormats.push({ ...group, formats: groupFormats });
}
});
return otherFormats;
}

$: displayType = type === 'bg' ? 'Background' : 'Foreground';
$: otherFormats = FORMATS.filter((s) => s !== format);
$: otherFormats = otherFormatGroups(format);
</script>

<div data-content="formats" data-column="tool">
<h4 class="small-only label">{displayType} Color</h4>
{#each otherFormats as format (format)}
<Output {type} {color} {format} />
<h2 class="small-only label">{displayType} Color</h2>
stacyk marked this conversation as resolved.
Show resolved Hide resolved
{#each otherFormats as formatGroup}
<FormatGroup {type} {color} {formatGroup} />
{/each}
</div>

<style lang="scss">
[data-content~='formats'] {
margin-bottom: var(--double-gutter);
margin-block-end: var(--double-gutter);
}

.label {
--label-margin-bottom: var(--gutter);
.small-only {
--label-margin-block-end: var(--gutter);
}
</style>
2 changes: 1 addition & 1 deletion src/lib/components/colors/Header.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@
}

[data-label] {
--label-margin-bottom: var(--half-shim);
--label-margin-block-end: var(--half-shim);
grid-area: label;
}

Expand Down
26 changes: 7 additions & 19 deletions src/lib/components/colors/Output.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { inGamut, serialize, to } from 'colorjs.io/fn';
import { serialize, to } from 'colorjs.io/fn';
import type { PlainColorObject } from 'colorjs.io/types/src/color';

import SupportWarning from '$lib/components/colors/SupportWarning.svelte';
Expand All @@ -13,40 +13,28 @@

$: targetSpace = getSpaceFromFormatId(format);
$: targetColor = to(color, targetSpace);
$: isInGamut = inGamut(targetColor);
$: targetColorValue = serialize(targetColor, {
format,
inGamut: false,
});
</script>

<ul data-group="output {type}">
<li>
<li data-testid={`format-${format}`}>
<CopyButton text={targetColorValue} />
<span data-color-info="value">{targetColorValue}</span>
<SupportWarning {format} />
{#if !isInGamut}
<span data-color-info="warning"
>This color is outside the {targetColor.space.name} gamut.</span
>
{/if}
</li>
</ul>

<style lang="scss">
[data-group~='output'] {
grid-column: 1 / -1;
}

li {
column-gap: 1ch;
display: grid;
grid-template:
'copy color' auto
'copy message' 3ex / auto 1fr;
}

[data-color-info='value'] {
grid-area: color;
}

[data-color-info='warning'] {
grid-area: message;
grid-template-columns: auto 1fr;
}
</style>
2 changes: 1 addition & 1 deletion src/lib/components/colors/Sliders.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
}

[data-group~='sliders'] {
--label-margin-bottom: var(--half-shim);
--label-margin-block-end: var(--half-shim);
margin-bottom: var(--triple-gutter);
}

Expand Down
1 change: 0 additions & 1 deletion src/lib/components/colors/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
<Sliders type="fg" color={fg} format={$format} />
</form>

<h3 class="label section-heading">Additional Formats</h3>
<div data-layout="split">
<Formats type="bg" color={$bg} format={$format} />
<Formats type="fg" color={$fg} format={$format} />
Expand Down
11 changes: 7 additions & 4 deletions src/lib/components/ratio/index.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
</div>

<div class="contrast-defined">
<h4 class="label">AA Contrast Ratio</h4>
<h3 class="label">AA Contrast Ratio</h3>
stacyk marked this conversation as resolved.
Show resolved Hide resolved

<dl>
<dt><strong>{RATIOS.AA.Normal}</strong> : 1</dt>
Expand All @@ -47,7 +47,7 @@
<dd>Large Text</dd>
</dl>

<h4 class="label">AAA Contrast Ratio</h4>
<h3 class="label">AAA Contrast Ratio</h3>
<dl>
<dt><strong>{RATIOS.AAA.Normal}</strong> : 1</dt>
<dd>Normal Text</dd>
Expand All @@ -56,7 +56,7 @@
<dd>Large Text</dd>
</dl>

<h4 class="label">Large Text Size</h4>
<h3 class="label">Large Text Size</h3>
<dl>
<dt><strong>≥ 24px</strong></dt>
<dd>Regular Weight</dd>
Expand Down Expand Up @@ -135,8 +135,11 @@
align-items: start;
display: inline-flex;
grid-area: number;
justify-content: flex-end;
line-height: 0.7; // weird number alignment

@include config.between('sm-column-break', 'lg-page-break') {
justify-content: flex-end;
}
}

.result-ratio-number {
Expand Down
23 changes: 23 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@ export const FORMATS: ColorFormatId[] = [
'srgb',
];

export type FormatGroup = {
name: string;
formats: ColorFormatId[];
gamutFormat?: ColorFormatId;
gamutName?: string;
};

export const FORMAT_GROUPS: FormatGroup[] = [
{
name: 'sRGB FORMATS',
stacyk marked this conversation as resolved.
Show resolved Hide resolved
formats: ['hex', 'hsl', 'srgb'],
gamutFormat: 'srgb',
gamutName: 'sRGB',
},
{ name: 'UNBOUNDED SPACES', formats: ['lab', 'lch', 'oklab', 'oklch'] },
{
name: 'DISPLAY P3 SPACE',
formats: ['p3'],
gamutFormat: 'p3',
gamutName: 'P3',
},
];

export const RATIOS = {
AA: {
Normal: 4.5,
Expand Down
7 changes: 2 additions & 5 deletions src/sass/initial/_type.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@

h1,
h2,
h3,
h4 {
h3 {
font-weight: normal;
margin: 0;
}
Expand All @@ -42,15 +41,13 @@ h4 {
display: block;
font-size: var(--heading-size, var(--label-size));
letter-spacing: var(--heading-letterspacing, 0.05rem);
margin-bottom: var(--label-margin-bottom, 0);
margin-block-end: var(--label-margin-block-end, 0);
text-transform: var(--heading-transform, uppercase);
}

.section-heading {
@include heading;

--label-margin-bottom: var(--gutter-plus);

@include config.below('sm-page-break') {
--heading-size: var(--medium);
}
Expand Down
6 changes: 6 additions & 0 deletions test/js/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ export const HSL_BLACK: PlainColorObject = {
};

export const HSL_WHITE_SERIALIZED = serialize(HSL_WHITE, { inGamut: false });

export const OUT_OF_BOUNDED_GAMUTS: PlainColorObject = {
space: ColorSpace.get('oklch'),
coords: [1, 1, 1],
alpha: 1,
};
36 changes: 36 additions & 0 deletions test/js/lib/components/colors/FormatGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { render } from '@testing-library/svelte';

import FormatGroup from '$lib/components/colors/FormatGroup.svelte';
import { FORMAT_GROUPS } from '$src/lib/constants';
import { HSL_WHITE, OUT_OF_BOUNDED_GAMUTS } from '$test/fixtures';

describe('FormatGroup', () => {
it('renders selected group', () => {
const FORMAT_GROUP = FORMAT_GROUPS[0]!;
const { getByTestId, getByText } = render(FormatGroup, {
type: 'bg',
color: HSL_WHITE,
formatGroup: FORMAT_GROUP,
});

expect(getByText(FORMAT_GROUP.name)).toBeVisible();
FORMAT_GROUP.formats.forEach((format) => {
expect(getByTestId(`format-${format}`)).toBeVisible();
});
});

it('renders warning if out of gamut', () => {
const FORMAT_GROUP = FORMAT_GROUPS[0]!;
const { getByTestId, getByText } = render(FormatGroup, {
type: 'bg',
color: OUT_OF_BOUNDED_GAMUTS,
formatGroup: FORMAT_GROUP,
});

expect(getByText(FORMAT_GROUP.name)).toBeVisible();
FORMAT_GROUP.formats.forEach((format) => {
expect(getByTestId(`format-${format}`)).toBeVisible();
});
expect(getByText('outside the sRGB gamut.')).toBeVisible();
});
});
17 changes: 0 additions & 17 deletions test/js/lib/components/colors/Output.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { render } from '@testing-library/svelte';
import { serialize, to } from 'colorjs.io/fn';
import type { PlainColorObject } from 'colorjs.io/types/src/color';

import Output from '$lib/components/colors/Output.svelte';
import { ColorSpace } from '$lib/stores';
import { HSL_WHITE, HSL_WHITE_SERIALIZED } from '$test/fixtures';

describe('Output', () => {
Expand All @@ -28,19 +26,4 @@ describe('Output', () => {
getByText(serialize(to(HSL_WHITE, 'oklch'), { inGamut: false })),
).toBeVisible();
});

it('renders warning if out of gamut', () => {
const color: PlainColorObject = {
space: ColorSpace.get('oklch'),
coords: [0.01, 0.02, 0],
alpha: 1,
};
const { getByText } = render(Output, {
type: 'fg',
color,
format: 'hsl',
});

expect(getByText('outside the HSL gamut', { exact: false })).toBeVisible();
});
});