# SCSS Helpers

`@orioncactuscorp/ui`가 SCSS source로 제공하는 consumer용 mixin/function입니다. 각 모듈은 `@orioncactuscorp/ui/scss/*` subpath로 import하고, `@use ... as *;` 형태를 사용합니다.

```scss
@use '@orioncactuscorp/ui/scss/mixins/element' as *;
@use '@orioncactuscorp/ui/scss/mixins/breakpoint' as *;
```

> `@orioncactuscorp/ui/scss/index.scss` 통합 entry는 mixin뿐 아니라 **foundation CSS(토큰 + reset)를 함께 방출**합니다. `foundations.css`를 이미 import하고 있다면 토큰이 중복 방출되므로, mixin/function만 필요할 때는 위처럼 개별 mixin 모듈을 import하세요. 통합 entry는 prebuilt `foundations.css` 대신 SCSS foundation source를 직접 compile하는 경로에서만 사용합니다.

이 문서에 있는 helper가 공개 계약입니다. 문서화되지 않은 export는 내부 구현이며 예고 없이 바뀔 수 있습니다([Stability Policy](./stability.md)).

## oc-border — layout에 영향 없는 surface stroke

`@orioncactuscorp/ui/scss/mixins/element`

실제 CSS `border` 대신 `box-shadow`로 stroke를 그립니다. 요소의 content/padding/layout 크기를 바꾸지 않습니다.

```scss
@include oc-border($tokens...);
```

CSS `border` shorthand와 비슷하게 토큰을 순서 무관하게 나열합니다.

| 토큰      | 값                                                  | 기본값         | 비고                                                                   |
| --------- | --------------------------------------------------- | -------------- | ---------------------------------------------------------------------- |
| width     | number (1개)                                        | `1px`          | `1.5px` 같은 half-pixel은 WebKit/DPR 보정 출력 포함                    |
| color     | color, `var(...)` (1개)                             | `currentColor` |                                                                        |
| side      | `top`, `right`, `bottom`, `left` (복수 가능), `all` | `all`          | 개별 side는 해당 방향 단일 stroke                                      |
| placement | `inset`, `outer`                                    | `inset`        | `outer`는 요소 바깥에 그림 (역시 layout 무영향)                        |
| style     | `solid`                                             | -              | CSS shorthand 익숙함을 위해 수용만 함 (box-shadow stroke는 항상 solid) |

```scss
.surface {
  @include oc-border; // 1px currentColor inset
}

.surfaceAccent {
  @include oc-border(var(--oc-color-theme-primary-normal));
}

.surfaceInvalid {
  @include oc-border(1.5px, var(--oc-color-theme-status-negative));
}

.underline {
  @include oc-border(bottom); // 아래쪽만
}

.focusRing {
  @include oc-border(2px, var(--oc-color-theme-primary-normal), outer);
}
```

`box-shadow` 한 채널을 사용하므로, 같은 요소에서 다른 `box-shadow`와 함께 쓸 때는 직접 shadow 목록을 조합해야 합니다.

## oc-radius — 전역 radius factor 연동

`@orioncactuscorp/ui/scss/mixins/radius`

```scss
oc-radius($radius, $factor-token: var(--oc-atomic-radius-factor))
```

`rem`/`em`/`px`/`clamp(...)` 기반 radius 값을 전역 radius factor에 연결합니다. `--oc-atomic-radius-factor`를 `0`으로 두면 이 helper를 거친 컴포넌트 로컬 radius도 함께 각지게 됩니다.

```scss
@use '@orioncactuscorp/ui/scss/mixins/radius' as *;

.badge {
  --oc-badge-radius: #{oc-radius(0.4em)};
}
```

## oc-focus-ring — 키보드 포커스 ring

`@orioncactuscorp/ui/scss/mixins/focus`

```scss
oc-focus-ring($color: var(--oc-color-theme-focus-ring), $width: var(--oc-focus-ring-width), $offset: var(--oc-focus-ring-offset))
```

`:focus-visible` ring을 foundation 토큰으로 그립니다. `outline` 기반이라 layout에 영향이 없고 요소의 border-radius를 그대로 따라가며 forced-colors 모드에서도 유지됩니다. reset 레이어가 `button`/`a[href]`/`summary`/`[tabindex]`에 같은 ring을 전역 기본값으로 제공하므로, 이 믹스인은 `all: unset`을 쓰는 요소나 ring을 다른 요소로 리다이렉트하는 경우(예: 선택 컨트롤의 wrapper)에 사용합니다.

```scss
@use '@orioncactuscorp/ui/scss/mixins/focus' as *;

.trigger {
  all: unset;

  &:focus-visible {
    @include oc-focus-ring;
  }
}
```

## oc-alpha — 토큰 기반 알파 색상

`@orioncactuscorp/ui/scss/mixins/color`

```scss
oc-alpha($color, $opacity)
```

`color-mix(in srgb, ...)`로 알파가 적용된 색을 만듭니다. runtime CSS 변수 색상에도 알파를 적용할 수 있다는 점이 Sass `rgba()`와의 차이입니다.

- `$color`: Sass color, `var(...)`, 또는 global color 토큰 suffix (`--oc-color-global-` 뒤 이름)
- `$opacity`: `0`~`1` 숫자, 또는 global opacity 토큰 suffix (`--oc-color-global-opacity-` 뒤 이름)

```scss
@use '@orioncactuscorp/ui/scss/mixins/color' as *;

.scrim {
  background-color: oc-alpha(var(--oc-color-theme-primary-normal), 0.4);
}
```

## oc-easing-gradient / oc-gradient-mask — 부드러운 gradient 경계

`@orioncactuscorp/ui/scss/mixins/gradient`

```scss
oc-easing-gradient($direction, $from, $to, $start: 0%, $end: 100%)

@include oc-gradient-mask(
  $direction,
  $start-fade-size: 0px,
  $end-fade-size: 0px,
  $content-size: 100%
);

@include oc-gradient-mask-edges(
  $top-fade-size: 0px,
  $right-fade-size: 0px,
  $bottom-fade-size: 0px,
  $left-fade-size: 0px,
  $inline-content-size: 100%,
  $block-content-size: 100%
);
```

`oc-easing-gradient`는 gradient line을 따라 색상을 선형 비율로 섞지 않고 smoothstep easing으로 샘플링한 중간 color stop을 생성합니다. 경계의 시작과 끝에서 색 변화 속도가 부드럽게 이어져 content fade, scrim, 이미지 overlay에 사용할 수 있습니다. 8개 구간은 부드러운 경계와 생성되는 CSS 크기 사이의 균형을 위한 고정값입니다.

- `$direction`: `top | right | bottom | left` 물리 방향
- `$from`, `$to`: Sass color, `transparent`, CSS `var(...)`
- `$start`, `$end`: gradient 변화가 시작하고 끝나는 위치

```scss
@use '@orioncactuscorp/ui/scss/mixins/gradient' as *;

.scrim {
  background-image: oc-easing-gradient(
    bottom,
    transparent,
    var(--oc-color-theme-background-normal),
    0%,
    var(--scrim-size)
  );
}
```

`oc-gradient-mask`는 같은 easing curve로 시작·끝 경계를 감추고 Safari용 `-webkit-mask-image`와 표준 `mask-image`를 함께 출력합니다. fade 길이와 전체 content 길이는 호출부가 소유한 CSS 변수를 전달해 runtime에 조정합니다. `$start-fade-size + $end-fade-size`는 `$content-size`를 넘지 않도록 설정하세요.

```scss
.scrollViewport {
  --example-start-fade-size: 0px;
  --example-end-fade-size: 3rem;
  --example-content-size: 100%;

  @include oc-gradient-mask(
    right,
    var(--example-start-fade-size),
    var(--example-end-fade-size),
    var(--example-content-size)
  );
}
```

2D 스크롤 영역은 `oc-gradient-mask-edges`로 block·inline mask를 교차 합성합니다. 앞의 네 인자는 CSS shorthand와 같은 `top → right → bottom → left` 순서이며 fade를 독립적으로 조절합니다. 표준 `mask-composite: intersect`와 WebKit용 `source-in`을 함께 출력합니다. 각 축의 양쪽 fade 합은 해당 content 길이를 넘지 않도록 설정하세요.

```scss
.canvasViewport {
  --example-top-fade-size: 0px;
  --example-right-fade-size: 3rem;
  --example-bottom-fade-size: 3rem;
  --example-left-fade-size: 0px;

  @include oc-gradient-mask-edges(
    var(--example-top-fade-size),
    var(--example-right-fade-size),
    var(--example-bottom-fade-size),
    var(--example-left-fade-size)
  );
}
```

마스크 상태를 전환할 때는 `mask-image` 자체를 transition하지 않고 fade 길이 custom property를 `@property`로 등록한 뒤 `oc-motion(..., fade)`를 적용합니다. gradient의 공간적 색 easing과 상태 변화의 시간적 motion을 별도 계약으로 유지합니다.

```scss
@use '@orioncactuscorp/ui/scss/mixins/motion' as *;

@property --example-start-fade-size {
  syntax: '<length>';
  inherits: false;
  initial-value: 0;
}

.scrollViewport {
  @include oc-motion(--example-start-fade-size, fade, $pace: quick);
}
```

## typo — typography 토큰 조합

`@orioncactuscorp/ui/scss/mixins/typo`

```scss
@include typo($variant, $weight: normal);
```

`font-size`, `line-height`, `letter-spacing`, `font-weight`를 `--oc-typo-*` 토큰에서 한 번에 출력합니다. raw `var(--oc-typo-size-*)` 직접 참조 대신 이 mixin을 사용하세요 — `--oc-typo-size-factor` runtime multiplier가 자동 반영됩니다.

- `$variant`: foundation typo variant 이름 (`title1`, `body1` 등 — 역할과 기본값은 [Typography](./typography.md))
- `$weight`: `normal` | `medium` | `bold`

19개 variant는 모두 `normal | medium | bold`를 제공하며, 의미상 같은 weight라도 작은 계층에서 숫자가 단계적으로 낮아집니다. 전체 weight·responsive stop 표는 [Typography](./typography.md)를 참조하세요.

```scss
@use '@orioncactuscorp/ui/scss/mixins/typo' as *;

.heading {
  @include typo(title2, bold);
}
```

## respond-to — breakpoint media query

`@orioncactuscorp/ui/scss/mixins/breakpoint`

```scss
@include respond-to($bp1, $bp2: null) { ... }
```

named breakpoint: `sm`(480px), `md`(768px), `lg`(1280px), `xl`(1600px), `hg`(1920px). `@use ... with ($breakpoint-md: 800px)`로 override할 수 있습니다.

| 호출                                                 | 출력 media                                                    | 용도                                 |
| ---------------------------------------------------- | ------------------------------------------------------------- | ------------------------------------ |
| `respond-to(sm)`, `respond-to(md)`                   | `(max-width: bp)`                                             | 좁은 viewport 예외 보정              |
| `respond-to(lg)`, `respond-to(xl)`, `respond-to(hg)` | `(min-width: bp + 1px)`                                       | 넓은 viewport 확장                   |
| `respond-to(min, $bp)`                               | `(min-width: bp + 1px)` (named) / `(min-width: 값)` (직접 값) | mobile-first progressive enhancement |
| `respond-to(max, $bp)`                               | `(max-width: bp)`                                             | 상한                                 |
| `respond-to($bp1, $bp2)`                             | `(min-width: 작은쪽 + 1px) and (max-width: 큰쪽)`             | 구간                                 |

직접 숫자 값(`respond-to(min, 900px)`)도 받습니다. raw `@media (min-width: ...)` 대신 항상 이 mixin을 사용하세요.

oc-ui SCSS는 mobile-first가 기본입니다: base rule은 narrow viewport 기준으로 쓰고, desktop 이상 차이는 `respond-to(min, md)`에서 더합니다. `respond-to(md)`(max-width)는 `Mobile(md)` 전용 예외 보정에만 사용합니다.

## oc-fluid / toRem — fluid 값 유틸

`@orioncactuscorp/ui/scss/mixins/fluid`, `@orioncactuscorp/ui/scss/mixins/utils`

```scss
oc-fluid($min, $max, $bp, $anchor: to)
```

`clamp($min, <viewport 비례식>, $max)`를 출력합니다. `$min`과 `$max`는 `px` 또는 `rem` 단위 값을 사용하고, `$bp`는 기준 viewport(px)입니다. `$bp`에는 Sass breakpoint 변수 또는 직접 입력한 값(`375px`, `375`)을 사용할 수 있습니다. `$anchor`는 비례식이 어느 끝값에 정렬될지 정하며 기본값은 `to`입니다.

```scss
@use '@orioncactuscorp/ui/scss/mixins/fluid' as *;
@use '@orioncactuscorp/ui/scss/mixins/breakpoint' as *;
@use '@orioncactuscorp/ui/scss/mixins/utils' as *;

.hero {
  padding-block: oc-fluid(toRem(24), toRem(64), 1280px);
}

.radius {
  border-radius: oc-fluid(toRem(10), toRem(12), $breakpoint-md, from);
}
```

`anchor: to`는 `$bp`에서 `$max`에 도달하며, `anchor: from`은 `$bp`에서 `$min`을 기준으로 이후 viewport에서 증가합니다. `oc-fluid()`는 값 생성 함수이므로 `font-size`, `line-height`, `padding`, `gap`, `border-radius` 등 CSS length 속성에 조합해서 사용할 수 있습니다.

`toRem($px)`는 `16px = 1rem` 기준으로 숫자를 rem으로 변환합니다.

## Motion / Spring

transition·animation helper(`oc-motion`, `oc-spring`)는 별도 가이드를 참조하세요.

- [Semantic Motion](./motion.md) — intent/phase/pace 기반 motion 계약
- [Spring Motion](./spring.md) — iOS perceptual duration 기반 spring transition
