Skip to content

Commit

Permalink
feat!: new brand color (#815)
Browse files Browse the repository at this point in the history
  • Loading branch information
amje committed Jul 14, 2023
1 parent fe3e5d0 commit 42e75ef
Show file tree
Hide file tree
Showing 22 changed files with 1,238 additions and 920 deletions.
2 changes: 1 addition & 1 deletion src/components/Button/Button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ $iconWidth: 16px;
--yc-button-background-color: var(--g-color-base-selection);
--yc-button-background-color-hover: var(--g-color-base-selection-hover);

@include button-text-color(var(--g-color-text-brand));
@include button-text-color(var(--g-color-text-brand-heavy));

&::before {
border: none;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export function BrandingConfigurator({theme}: BrandingConfiguratorProps) {
<div className={b('result')}>
<Card view="filled" theme="normal" className={b('result-card')}>
<div className={b('result-text')}>{resultText}</div>
<ClipboardButton text={resultText} size={28} className={b('result-copy')} />
<ClipboardButton text={resultText} size={16} className={b('result-copy')} />
</Card>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions src/stories/Branding/Overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ list of such variables:
* `--g-color-base-brand-hover` - Hover color for background
* `--g-color-line-brand` - Color for lines (active tab underline)
* `--g-color-text-brand` - Text color
* `--g-color-text-brand-heavy` - Text color over background
* `--g-color-text-brand-contrast` - Text color over brand background
* `--g-color-base-selection` - Lighter version for background (List selection, Table row selection)
* `--g-color-base-selection-hover` - Hover lighter version for background
Expand Down
15 changes: 15 additions & 0 deletions src/stories/Branding/PaletteGenerator.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

import type {Meta, StoryFn} from '@storybook/react';

import {PaletteGenerator} from './PaletteGenerator/PaletteGenerator';

export default {
title: 'Branding/Palette Generator',
} as Meta;

export const Default: StoryFn = (_, ctx) => {
return <PaletteGenerator theme={ctx.globals.theme} />;
};

Default.storyName = 'Palette Generator';
98 changes: 98 additions & 0 deletions src/stories/Branding/PaletteGenerator/PaletteGenerator.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
@use '../../../../styles/mixins';

.palette-generator {
@include mixins.text-body-2;

&__title {
@include mixins.text-display-1;
margin-bottom: 16px;

&:not(:first-child) {
margin-top: 32px;
}
}

&__parameters {
display: grid;
grid-template-columns: repeat(2, max-content);
align-items: center;
gap: 10px 20px;

&-name {
@include mixins.text-subheader-3;
}
}

&__color-picker {
flex-shrink: 0;
display: inline-block;
position: relative;
width: 40px;
height: 40px;
border-radius: 5px;
border: 2px solid var(--g-color-line-generic);
background-clip: content-box;
cursor: pointer;

&-wrapper {
display: flex;
align-items: center;
gap: 10px;
}

&-input {
position: absolute;
left: 0;
bottom: 0;
width: 0;
height: 0;
visibility: hidden;
opacity: 0;
}
}

&__palette {
&-grid {
display: grid;
grid-template-columns: min-content 80px 80px;
grid-auto-rows: 40px;
}

&-item {
&-number {
align-self: center;
justify-self: end;
padding-right: 10px;
}

&-color {
position: relative;

&-inner {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
}
}
}

&__result-card {
position: relative;
width: 500px;
padding: 16px;
}

&__result-copy {
position: absolute;
top: 12px;
right: 12px;
}

&__result-text {
@include mixins.text-code-1;
white-space: pre;
}
}
206 changes: 206 additions & 0 deletions src/stories/Branding/PaletteGenerator/PaletteGenerator.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import React from 'react';

import {ArrowUpArrowDown} from '@gravity-ui/icons';
import block from 'bem-cn-lite';
import chroma from 'chroma-js';

import {Button, Card, ClipboardButton, Icon, TextInput} from '../../../components';

import './PaletteGenerator.scss';

export interface BrandingConfiguratorProps {
theme: string;
}

const b = block('palette-generator');
const colorsMap = {
50: {a: 0.1, c: -1},
100: {a: 0.15, c: -1},
150: {a: 0.2, c: -1},
200: {a: 0.3, c: -1},
250: {a: 0.4, c: -1},
300: {a: 0.5, c: -1},
350: {a: 0.6, c: -1},
400: {a: 0.7, c: -1},
450: {a: 0.8, c: -1},
500: {a: 0.9, c: -1},
550: {a: 1, c: 1},
600: {a: 0.9, c: 1},
650: {a: 0.8, c: 1},
700: {a: 0.7, c: 1},
750: {a: 0.6, c: 1},
800: {a: 0.5, c: 1},
850: {a: 0.4, c: 1},
900: {a: 0.3, c: 1},
950: {a: 0.2, c: 1},
1000: {a: 0.15, c: 1},
};

const lowContrastBaseByTheme: Record<string, string> = {
light: 'rgba(255, 255, 255, 1)',
'light-hc': 'rgba(255, 255, 255, 1)',
dark: 'rgba(45, 44, 51, 1)',
'dark-hc': 'rgba(34, 35, 38, 1)',
};
const highContrastBaseByTheme: Record<string, string> = {
light: lowContrastBaseByTheme['dark'],
'light-hc': lowContrastBaseByTheme['dark-hc'],
dark: lowContrastBaseByTheme['light'],
'dark-hc': lowContrastBaseByTheme['light-hc'],
};

export function PaletteGenerator({theme}: BrandingConfiguratorProps) {
const [name, setName] = React.useState('color');
const colorTextRef = React.useRef<HTMLInputElement>(null);
const [color, setColor] = React.useState(chroma.random().hex());
const [lowContrastBase, setLowContrastBase] = React.useState(lowContrastBaseByTheme[theme]);
const [highContrastBase, setHighContrastBase] = React.useState(highContrastBaseByTheme[theme]);
const palette = React.useMemo(() => {
return Object.entries(colorsMap).reduce((res, [key, {a, c}]) => {
const i = Number(key);
const solidColor = chroma
.mix(color, c > 0 ? highContrastBase : lowContrastBase, 1 - a, 'rgb')
.css();
const alphaColor = i > 500 ? '' : chroma(color).alpha(a).css();

res[key] = [solidColor, alphaColor];

return res;
}, {} as Record<string, [string, string]>);
}, [color, lowContrastBase, highContrastBase]);

const resultText = React.useMemo(() => {
return `
@mixin g-colors-private-${name}-${theme} {
${Object.keys(palette)
.filter((key) => palette[key][1])
.map((key) => `--g-color-private-${name}-${key}: ${palette[key][1]};`)
.join('\n ')}
${Object.keys(palette)
.map((key) => `--g-color-private-${name}-${key}-solid: ${palette[key][0]};`)
.join('\n ')}
}
`.trim();
}, [name, palette, theme]);

const handleSwapContrastClick = React.useCallback(() => {
setLowContrastBase(highContrastBase);
setHighContrastBase(lowContrastBase);
}, [lowContrastBase, highContrastBase]);

const handleColorTextUpdate = React.useCallback((value: string) => {
if (chroma.valid(value)) {
setColor(value);
}
}, []);

React.useEffect(() => {
setLowContrastBase(lowContrastBaseByTheme[theme]);
setHighContrastBase(highContrastBaseByTheme[theme]);
}, [theme]);

React.useEffect(() => {
if (colorTextRef.current) {
colorTextRef.current.value = color;
}
}, [color]);

return (
<div className={b()}>
<div className={b('title')}>Parameters</div>
<div className={b('parameters')}>
<div className={b('parameters-name')}>Name</div>
<div className={b('parameters-control')}>
<TextInput value={name} onUpdate={setName} />
</div>
<div className={b('parameters-name')}>Main Color</div>
<div className={b('parameters-control')}>
<div className={b('color-picker-wrapper')}>
<label className={b('color-picker')} style={{backgroundColor: color}}>
<input
type="color"
className={b('color-picker-input')}
value={color}
onChange={(e) => setColor(e.target.value)}
/>
</label>
<TextInput
controlRef={colorTextRef}
defaultValue={color}
onUpdate={handleColorTextUpdate}
/>
</div>
</div>
<div className={b('parameters-name')}>Low Contrast Base</div>
<div className={b('parameters-control')}>
<label className={b('color-picker')} style={{backgroundColor: lowContrastBase}}>
<input
type="color"
className={b('color-picker-input')}
value={lowContrastBase}
onChange={(e) => setLowContrastBase(e.target.value)}
/>
</label>
</div>
<div className={b('parameters-name')} />
<div className={b('parameters-control')}>
<Button view="outlined" size="l" onClick={handleSwapContrastClick}>
<Icon data={ArrowUpArrowDown} size={18} />
</Button>
</div>
<div className={b('parameters-name')}>High Contrast Base</div>
<div className={b('parameters-control')}>
<label
className={b('color-picker')}
style={{backgroundColor: highContrastBase}}
>
<input
type="color"
className={b('color-picker-input')}
value={highContrastBase}
onChange={(e) => setHighContrastBase(e.target.value)}
/>
</label>
</div>
</div>
<div className={b('title')}>Palette</div>
<div className={b('palette')}>
<div className={b('palette-grid')}>
{Object.entries(palette).map(([i, colors]) => {
return (
<React.Fragment key={i}>
<div className={b('palette-item-number')}>{i}</div>
<div
className={b('palette-item-color')}
style={{backgroundColor: lowContrastBase}}
>
<div
className={b('palette-item-color-inner')}
style={{backgroundColor: colors[0]}}
/>
</div>
<div
className={b('palette-item-color')}
style={{backgroundColor: lowContrastBase}}
>
<div
className={b('palette-item-color-inner')}
style={{backgroundColor: colors[1] || highContrastBase}}
/>
</div>
</React.Fragment>
);
})}
</div>
</div>
<div className={b('title')}>Result Code</div>
<div className={b('result')}>
<Card view="filled" theme="normal" className={b('result-card')}>
<div className={b('result-text')}>{resultText}</div>
<ClipboardButton text={resultText} size={16} className={b('result-copy')} />
</Card>
</div>
</div>
);
}
12 changes: 6 additions & 6 deletions styles/themes/dark-hc/base.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@mixin g-colors-base-dark-hc {
--g-color-base-background: var(--g-color-private-black-rock-950);
--g-color-base-background: rgb(18, 17, 18);
--g-color-base-generic: var(--g-color-private-white-100);
--g-color-base-generic-hover: var(--g-color-private-white-250);
--g-color-base-generic-medium: var(--g-color-private-white-250);
Expand All @@ -10,10 +10,10 @@
--g-color-base-simple-hover: var(--g-color-private-white-250);
--g-color-base-simple-hover-solid: var(--g-color-private-white-250-solid);

--g-color-base-selection: var(--g-color-private-blue-350);
--g-color-base-selection-hover: var(--g-color-private-blue-600-solid);
--g-color-base-brand: var(--g-color-private-blue-450-solid);
--g-color-base-brand-hover: var(--g-color-private-blue-650-solid);
--g-color-base-brand: var(--g-color-private-yellow-550-solid);
--g-color-base-brand-hover: var(--g-color-private-yellow-700-solid);
--g-color-base-selection: var(--g-color-private-yellow-150);
--g-color-base-selection-hover: var(--g-color-private-yellow-200);

--g-color-base-info-light: var(--g-color-private-blue-250);
--g-color-base-info-light-hover: var(--g-color-private-blue-400);
Expand Down Expand Up @@ -72,5 +72,5 @@

--g-color-base-float-announcement: var(--g-color-private-white-200-solid);

--g-color-base-modal: var(--g-color-private-black-rock-850);
--g-color-base-modal: var(--g-color-base-background);
}
4 changes: 2 additions & 2 deletions styles/themes/dark-hc/line.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
--g-color-line-generic-accent-hover: var(--g-color-private-white-800);
--g-color-line-generic-solid: var(--g-color-private-white-150-solid);

--g-color-line-brand: var(--g-color-private-blue-450-solid);
--g-color-line-brand: var(--g-color-private-yellow-600-solid);

--g-color-line-info: var(--g-color-private-blue-550-solid);
--g-color-line-positive: var(--g-color-private-green-550-solid);
--g-color-line-warning: var(--g-color-private-yellow-100);
--g-color-line-warning: var(--g-color-private-yellow-550-solid);
--g-color-line-danger: var(--g-color-private-red-550-solid);
--g-color-line-misc: var(--g-color-private-cool-grey-550-solid);

Expand Down
Loading

0 comments on commit 42e75ef

Please sign in to comment.