본문 바로가기

FrontEnd

CSS Variables를 이용하여 컬러 팔레트 구성하기

반응형

원문 : https://blog.maximeheckel.com/posts/the-power-of-composition-with-css-variables/

 

The Power of Composition with CSS Variables - Maxime Heckel's Blog

How leveraging CSS variable composition and HSLA colors helped me build a more efficient way to theme my apps and rely less on CSS-in-JS.

blog.maximeheckel.com

 

CSS 변수와 HSLA 색상을 사용한 좋은 CSS 습관

테마와 색상을 관리하기 위해 ThemeProviders에 지나치게 의존하지 마세요
JS기반 테마를 제거하고, CSS 파일에만 의존하여 변수를 삽입합니다.
// Before I was constantly using JS based theme with interpolation function:
const MyStyledComponent = styled('div')`
  background-color: ${(p) => p.theme.primaryColor};
  color: ${(p) => p.theme.typefaceColor1};
`;

// Now I, for anything theme related,
// I simply used CSS variables that are defined in a global CSS file:
const MyStyledComponent = styled('div')`
  background-color: var(--primaryColor);
  color: var(--typefaceColor1);
`;
--spacing-0: 4px;
--spacing-1: 8px;
.myclass {
  padding: var(--spacing-0) var(--spacing-1); /* equivalent to padding: 4px 8px; */
}

HEX 포맷 사용하기

HSLA는 Hue Saturation Lightness Alpha 채널을 의미합니다.
색상을 정의하는 데 필요한 네 가지 주요 구성 요소입니다.
Hue-색조는 색상환의 각 정도가 색상인 색상환에서 정의된 360 색상 팔레트에서 나옵니다.
채도, 밝기 및 알파 채널 속성은 백분율(%)로 정의되며 각각 다음을 나타냅니다.
  • saturation-채도 : 색상의 생동감: 0%가 가장 적은 생동감, 100%가 가장 큰 생동감
  • lightness-조명 강도: 0%가 가장 어둡고 100%가 가장 밝음
  • Alpha channel-불투명도: 0%는 투명하고 100%는 색상을 완전히 보이게 합니다.

 

원문 가서 사용해보셈

HSLA를 사용하면 여러 색상이 얼마나 가까운지 쉽게 알 수 있습니다.

유사한 색상이 동일한 hue(색조)와 saturationI(채도)를 공유한다는 것을 알 수 있습니다.

CSS 변수를 통해 색조와 채도의 일부만 정의하고 이를 재사용하여 다른 색상 변수를 정의할 수 있다면 어떨까요?

컬러 스케일

제 경력 전반에 걸쳐 함께 일한 디자이너들은 종종 다음과 같이 HSLA를 사용하여 색상 스케일을 구축했습니다.
  • 밝기(lightness)가 50%일 때 색상을 선택합니다. 이것은 스케일의 "중간" 색상이 됩니다.
  • 밝기를 10% 높여 해당 색상의 다음 "밝은 음영(shade)"을 만듭니다.
  • 밝기를 10% 줄여 다음 "해당 색상의 더 어두운 음영"을 만듭니다.
이를 통해 주어진 색상의 풀 컬러 스케일을 얻은 다음 앱에서 사용합니다.
이를 css로 옮겨봅시다.
--base-blue: 222, 89%; /* All my colors here share the same hue and saturation */
--palette-blue-10: hsla(var(--base-blue), 10%, 100%);
--palette-blue-20: hsla(var(--base-blue), 20%, 100%);
--palette-blue-30: hsla(var(--base-blue), 30%, 100%);
--palette-blue-40: hsla(var(--base-blue), 40%, 100%);
--palette-blue-50: hsla(var(--base-blue), 50%, 100%);
--palette-blue-60: hsla(var(--base-blue), 60%, 100%);
--palette-blue-70: hsla(var(--base-blue), 70%, 100%);
--palette-blue-80: hsla(var(--base-blue), 80%, 100%);
--palette-blue-90: hsla(var(--base-blue), 90%, 100%);
--palette-blue-100: hsla(var(--base-blue), 100%, 100%);
--base-blue CSS 변수가 여기에서 핵심입니다.
partial 값에 할당됩니다.
CSS 변수 자체는 CSS 값으로 사용할 수 없으며 합성을 통해서만 사용할 수 있습니다. 
  • 내 파란색 스케일의 나머지 부분을 매우 쉽게 합성합니다.
  • 기본 색상을 변경하여 색상 스케일을 완전히 업데이트합니다.

아래는 이 패턴이 얼마나 ✨멋진✨인지 보여주는 작은 위젯입니다. 단일 변수를 조정하여 전체 색상 스케일을 생성할 수 있습니다!

이 기술을 통해 단순히 "멋져 보이는" 임의의 색상을 선택하는 대신 이해할 수 있는 색상을 생성할 수 있었습니다.

색상을 의미에 따라 합성하기

중요한 것은 색상의 의미와 실제 값입니다.

--base-blue: 222, 89%; /* All my colors here share the same hue and saturation */
/*
  Here I declared my color palette.
  Each color is a partial value
*/
--palette-blue-10: var(--base-blue), 10%;
--palette-blue-20: var(--base-blue), 20%;
--palette-blue-30: var(--base-blue), 30%;
--palette-blue-40: var(--base-blue), 40%;
--palette-blue-50: var(--base-blue), 50%;
--palette-blue-60: var(--base-blue), 60%;
--palette-blue-70: var(--base-blue), 70%;
--palette-blue-80: var(--base-blue), 80%;
--palette-blue-90: var(--base-blue), 90%;
--palette-blue-100: var(--base-blue), 100%;
/*
  Here I compose a color based on its meaning:
  - primary and emphasis are composed by using --palette-blue-50
  - they use a different opacity level thus they have different meaning:
    - primary is a bold color used for buttons and primary CTA
    - emphasis is used to highlight content or for the background of my callout cards
*/
--primary: hsla(var(--palette-blue-50), 100%);
--emphasis: hsla(var(--palette-blue-50), 8%);
  • 동일한 partial value에서 상속되는 2개의 서로 다른 색상 변수를 정의하여 일부 계층 구조를 생성할 수 있습니다.
  • 컴포넌트 스타일을 더 일반적/추상적으로 만듭니다.

고급 구성 패턴

다음 시나리오를 상상해 봅시다.
  • 내 앱에는 기본 색상의 색조(hue)와 채도(saturation)를 정의하는 --base-primary 변수가 있는 기본 테마가 있습니다.
  • 해당 앱 내의 버튼에는 --base-primary로 구성된 배경색과 50%의 밝기가 있습니다.
  • 해당 버튼의 호버 및 포커스 색상은 각각 기본 색상보다 10%, 20% 어둡고, 합성을 통해 정의됩니다.
  • 사용자가 원하는 기본 색상을 선택할 수 있습니다. 이는 다른 색조, 채도 및 밝기(lightness)를 의미합니다.
/**
  Our set of CSS variables in this case is as follows:
  --base-primary: 222, 89%;
  --primary: hsla(var(--base-primary), 50%, 100%);
  --primary-hover: hsla(var(--base-primary), 40%, 100%);
  --primary-focus: hsla(var(--base-primary), 30%, 100%);
  --text-color: hsla(0, 0%, 100%, 100%);
**/
const StyledButton = styled('button')`
  height: 45px;
  border-radius: 8px;
  box-shadow: none;
  border: none;
  padding: 8px 16px;
  text-align: center;
  color: var(--text-color);
  background-color: var(--primary);
  cursor: pointer;
  font-weight: 500;
  &:hover {
    background-color: var(--primary-hover);
  }
  &:focus {
    background-color: var(--primary-focus);
  }
`;

사용자가 디폴트 밝기를 50%가 아닌 것으로 선택하면 문제가 복잡해집니다.

따라서 밝기를 사용자 입력값에 기반해 계산합니다.

  • 기본 색상의 색조와 채도를 포함하기 전에 --base-primary를 정의합니다.
  • 기본 색상의 기본 밝기를 포함하는 --base-primary-lightness를 정의합니다.
  • 호버, 포커스 시의 밝기를 계산합니다
/**
  You can see here that we're only passing CSS variables for the background color
  for the hover, focus and rest state of the Button.
**/
const StyledButton = styled('button', {
  height: '45px',
  borderRadius: '8px',
  boxShadow: 'none',
  border: 'none',
  padding: '8px 16px',
  textAlign: 'center',
  color: 'var(--text-color)',
  backgroundColor: 'var(--primary)',
  cursor: 'pointer',
  fontWeight: '500',
  '&:hover': {
    backgroundColor: 'var(--primary-hover)',
  },
  '&:focus': {
    backgroundColor: 'var(--primary-focus)',
  },
});

/**
  You can try to modify the lightness or base hue/saturation below.
  You should see that the button hover and focus color will adapt and take into account
  almost (see below why) any color!
**/
const BlueThemeWrapper = styled('div', {
  '--base-primary': '222, 89%',
  '--base-primary-lightness': '50%',
  '--primary': 'hsla(var(--base-primary), var(--base-primary-lightness), 100%)',
  '--primary-hover': `hsla(
    var(--base-primary),
    calc(var(--base-primary-lightness) - 10%),
    /* --primary-hover is --primary but 10% darker */ 100%
  )`,
  '--primary-focus': `hsla(
    var(--base-primary),
    calc(var(--base-primary-lightness) - 20%),
    /* --primary-hover is --primary but 20% darker */ 100%
  )`,
  '--text-color': 'hsla(0, 0%, 100%, 100%)',
});

const CyanThemedWrapper = styled('div', {
  '--base-primary': '185, 75%',
  '--base-primary-lightness': '60%',
  '--primary': 'hsla(var(--base-primary), var(--base-primary-lightness), 100%)',
  '--primary-hover': `hsla(
    var(--base-primary),
    calc(var(--base-primary-lightness) - 10%),
    100%
  )`,
  '--primary-focus': `hsla(
    var(--base-primary),
    calc(var(--base-primary-lightness) - 20%),
    100%
  )`,
  '--text-color': 'hsla(0, 0%, 100%, 100%)',
});

const RedThemeWrapper = styled('div', {
  '--base-primary': '327, 80%',
  '--base-primary-lightness': '40%',
  '--primary': 'hsla(var(--base-primary), var(--base-primary-lightness), 100%)',
  '--primary-hover': `hsla(
    var(--base-primary),
    calc(var(--base-primary-lightness) - 10%),
    100%
  )`,
  '--primary-focus': `hsla(
    var(--base-primary),
    calc(var(--base-primary-lightness) - 20%),
    100%
  )`,
  '--text-color': 'hsla(0, 0%, 100%, 100%)',
});

const ThemedButton = () => {
  return (
    <div
      style={{
        display: 'flex',
        flexDirection: 'column',
        justifyContent: 'space-between',
        height: '175px',
      }}
    >
      <BlueThemeWrapper>
        <StyledButton>Primary Button</StyledButton>
      </BlueThemeWrapper>
      <CyanThemedWrapper>
        <StyledButton>Primary Button</StyledButton>
      </CyanThemedWrapper>
      <RedThemeWrapper>
        <StyledButton>Primary Button</StyledButton>
      </RedThemeWrapper>
    </div>
  );
};
render(<ThemedButton />);
 
위의 이 기술은 매우 훌륭하지만 한계가 있습니다.
  • 사용자가 너무 어두운 색상을 정의하면 호버 및 포커스 배경 색상이 잘 보이지 않습니다.
    • 테마 중 하나의 --base-primary-lightness를 5%로 수정하여 위에서 시도해 볼 수 있습니다.
  • 색상이 너무 밝으면 또 다른 문제가 발생합니다.
    • 텍스트가 버튼 안에서 흰색이며,이를 고려해야 합니다.
    • 테마 중 하나의 --base-primary-lightness를 95%로 수정하여 위에서 시도해 볼 수 있습니다.
SaaS를 사용하여 이러한 문제를 쉽게 해결할 수 있긴 합니다.


참고

tailwind css의 idea

Composing the Uncomposable with CSS variables

 

Composing the Uncomposable with CSS Variables – Adam Wathan

Many CSS properties are shorthands for a set of other properties, for example the margin property is a shorthand for setting margin-top, margin-right, margin-bottom, and margin-left all at once. Because the margin property decomposes into those four separa

adamwathan.me

 

반응형