# 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(),
  })),
});
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://taewoongs-organization.gitbook.io/jtwjs-dev-wiki/dev_road/week-8/global-style-and-theme.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
