> For the complete documentation index, see [llms.txt](https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/dev_road/week-8/global-style-and-theme.md).

# Global Style & Theme

## Global Style

> 전역으로 스타일을 지정

### createGlobalStyle

> 전역 스타일을 처리하는 특별한 스타일드 컴포넌트를 생성하는 헬퍼 함수

일반적인 스타일드 컴포넌트는 자동으로 클래스 범위가 지정되어 다른 컴포넌트로부터 격리되지만 `createGlobalStyle` 은 이러한 제한이 없어서 전역 스타일링이 가능하다.

전역 스타일에 reset css를 추가하여 초기화도 가능하다.

[The 62.5% Font Size Trick](https://www.aleksandrhovhannisyan.com/blog/62-5-percent-font-size-trick/)

```typescript
// styles/GlobalStyle.ts
import { createGlobalStyle } from 'styled-components';
import reset from 'styled-reset';

const GlobalStyle = createGlobalStyle`
  ${reset}
  html {
    box-sizing: border-box;
  }

  *,
  *::before,
  *::after {
    box-sizing: inherit;
  }

  html {
  // 브라우저의 폰트 크기 설정과 연동되는 rem 단위를 사용,
  // rem 단위를 계산하기 쉽게 하기 위해 기본 폰트를 62.5%(10px)로 설정한다.
    font-size: 62.5%; // 10px, (100% === 16px)
  }

  body {
    font-size: 1.6rem;
  }

  :lang(ko) {
  // 한글로 작성된 요소 중 헤딩 태그에 의도치 않은 줄바꿈을 방지하기 위해 설정
    h1, h2, h3 {
      word-break: keep-all;
    }
  }
`;

export default GlobalStyle;
```

```typescript
import GlobalStyle from './styles/GlobalStyle';

export default function App() {
  return (
    <>
      <GlobalStyle />
      <Greeting />
    </>
  );
}
```

{% hint style="info" %}
**Reset CSS**란 브라우저에 기본적으로 설정된 스타일을 제거하기 위해 사용된다.

#### Install

```bash
npm i styled-reset
```

#### Usage

```typescript
import { Reset } from 'styled-reset';

export default function App() {
  return (
    <>
      <Reset />
      <Greeting />
    </>
  );
}
```

{% endhint %}

## Theme

> 디자인 시스템 전체에서 사용되는 색상, 타이포그래피, 아이콘 등의 디자인 요소의 집합

테마를 정의할 때 디자인 시스템에서 사용되는 컬러 팔레트, 타이포그래피, 간격 등을 토큰으로 관리하고, 이를 이용해서 디자인 시스템 전체의 일관성 있는 디자인을 유지하고 작업의 효율성을 높힌다.

{% hint style="info" %}
[**디자인 토큰(token)**](https://medium.com/eightshapes-llc/tokens-in-design-systems-25dd82d58421)이란 디자인 요소의 속성 값을 변수화하여 관리하는 방식
{% endhint %}

### Token Naming

> 눈에 보이는 단편적인 정보를 넘어서, “의미”에 집중

디자인 시스템 사용자가 특정 토큰의 디자인 의도와 사용법을 이해할 수 있도록 하는 것이 가장 중요하다.

이러한 철학은 의도에 따른 디자인을 가능케 한다.

컬러 토큰을 예로 들면 색상 값에 얽매이지 않고 의도가 더 잘 담긴 추상화된 네이밍을 사용하는 것이 좋다,

추후 색상값이 변경되는 상황이 생겨도 코드 변경이 쉽고, 유지보수성이 높다.&#x20;

하지만 어떤 경우는 의도가 담긴 네이밍을 사용하기 어려울 수도 있다. 중요한건 일관성과 가독성이다.

### Theme Provider

> 스타일드 컴포넌트에서 제공되는 ThemeProvider는 내부적으로 Context API를 사용하여 자식들에게 테마 데이터를 제공한다.&#x20;

```tsx
import { ThemeProvider } from 'styled-components';

import { GlobalStyle, theme } from '@/styles';

const App = () => (
  <ThemeProvider theme={theme}>
    <GlobalStyle />
    <Greeting />
  </ThemeProvider>
);

export default App;
```

### 테마 타입 선언 병합

> 스타일 컴포넌트 타입 정의 파일에 선언된 테마 인터페이스 타입을 사용자가 만든 테마의 타입으로 확장하려면, 선언 병합을 사용해야 한다.

사용자가 정의한 테마에서 역으로 타입을 추출해서 확장하면 편-하다.

#### `styled.d.ts`

```typescript
import 'styled-components';

import { defaultTheme } from '@/styles';

type Theme = typeof defaultTheme;

declare module 'styled-components' {
  export interface DefaultTheme extends Theme {}
}
```

{% hint style="info" %}
타입 속성을 확장하는 것을 **선언 병합(declaration merging)**&#xC774;라 한다.\
같은 interface를 재선언 함으로써 타입 속성을 추가시킬 수 있다. → **보강**
{% endhint %}

{% hint style="info" %}
`.d.ts`는 TypeScript에서 타입 정의(type definition)를 위한 파일 확장자,

`declare module`은 Typescript에서 외부 모듈에 대한 타입 선언을 제공하는 구문
{% endhint %}

### DarkMode 구현하기

> 테마를 갈아끼우는 식으로 다크모드를 구현 가능

#### `useDarkTheme`

```typescript
import { useLocalStorage, useMediaQuery, useUpdateEffect } from 'usehooks-ts'

const COLOR_SCHEME_QUERY = '(prefers-color-scheme: dark)'

interface UseDarkModeOutput {
  isDarkMode: boolean
  toggle: () => void
  enable: () => void
  disable: () => void
}

function useDarkMode(defaultValue?: boolean): UseDarkModeOutput {
  const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY)
  const [isDarkMode, setDarkMode] = useLocalStorage<boolean>(
    'usehooks-ts-dark-mode',
    defaultValue ?? isDarkOS ?? false,
  )

  // Update darkMode if os prefers changes
  useUpdateEffect(() => {
    setDarkMode(isDarkOS)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isDarkOS])

  return {
    isDarkMode,
    toggle: () => setDarkMode(prev => !prev),
    enable: () => setDarkMode(true),
    disable: () => setDarkMode(false),
  }
}

export default useDarkMode
```

```tsx
export default function App() {
const { isDarkMode, toggle } = useDarkMode();

const theme = isDarkMode ? darkTheme : defaultTheme;

return (
  <ThemeProvider theme={theme}>
    <GlobalStyle />
    <Greeting />
    <Button onClick={toggle}>
      Dark Theme Toggle
    </Button>
  </ThemeProvider>
  );
}
```

### jest '`window.matchMedia`' 오류

> jest에서 사용되는 [jsdom에서 구현되지 않은 메서드를 사용할 때 오류](https://jestjs.io/docs/manual-mocks#mocking-methods-which-are-not-implemented-in-jsdom)를 발생시킨다.
>
> 이 경우 모킹을 통해 해결이 가능하다

#### `setupTests.ts`

```typescript
Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation((query) => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(), // deprecated
    removeListener: jest.fn(), // deprecated
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});
```
