@uniai-fe/uds-foundation 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,11 @@ pnpm add @uniai-fe/uds-foundation
10
10
 
11
11
  ## 사용법 요약
12
12
 
13
+ ### 최근 업데이트
14
+
15
+ - `--color-label-standard` 값을 Figma 최신 스펙(`#4f5258`)으로 맞추고 disabled/interaction 계열 토큰도 함께 재검증했다. npm 배포 전 `pnpm --filter @uniai-fe/uds-foundation build`를 수행해 CSS/SCSS와 `docs/theme-tokens.{md,json}` 스냅샷까지 동기화한다.
16
+ - Sass 소비자는 `@use "@uniai-fe/uds-foundation/css.scss";` 경로를 사용해야 하며, CSS-only 프로젝트는 기존대로 `import "@uniai-fe/uds-foundation/css";`로 토큰을 주입한다.
17
+
13
18
  ### 1) CSS 변수 주입
14
19
 
15
20
  ```ts
@@ -32,6 +37,27 @@ import "@uniai-fe/uds-foundation/css";
32
37
  }
33
38
  ```
34
39
 
40
+ ### Sass 유틸리티
41
+
42
+ Sass 소비자는 `@use "@uniai-fe/uds-foundation/scss";`로 전체 토큰을, `@use "@uniai-fe/uds-foundation/scss/functions" as style;`로 스타일 계산 함수를, `@use "@uniai-fe/uds-foundation/scss/responsive" as responsive;`로 반응형 rem 믹스인을 사용할 수 있다. alias(`as style`)를 지정하면 `style.padding()`처럼 짧은 이름으로 접근할 수 있다.
43
+
44
+ ```scss
45
+ @use "@uniai-fe/uds-foundation/scss" as foundation;
46
+ @use "@uniai-fe/uds-foundation/scss/functions" as style;
47
+ @use "@uniai-fe/uds-foundation/scss/responsive" as responsive;
48
+
49
+ :root {
50
+ @include foundation.foundation-layout-radius();
51
+ @include responsive.base();
52
+ }
53
+
54
+ @include responsive.queries();
55
+
56
+ .panel {
57
+ @include style.padding("rem", 16);
58
+ }
59
+ ```
60
+
35
61
  ### 2) 토큰 데이터(JSON)
36
62
 
37
63
  ```ts
package/dist/index.css CHANGED
@@ -17,7 +17,7 @@
17
17
  box-sizing: border-box;
18
18
  }
19
19
  * {
20
- letter-spacing: -0.05em;
20
+ letter-spacing: -0.02em;
21
21
  flex-shrink: 0;
22
22
  overscroll-behavior-y: none;
23
23
  font-family: var(--font-family-sans, "Pretendard JP Variable", "Pretendard JP", "Pretendard Variable", "Pretendard", "Inter", sans-serif);
@@ -712,5 +712,48 @@
712
712
  --font-caption-medium-line-height: 1.5em;
713
713
  --font-caption-medium-letter-spacing: 0px;
714
714
  --font-caption-medium-weight: 500;
715
+ --responsive-axis-min: min(0.520833vw, 0.925926vh);
716
+ --responsive-size: clamp(7px, var(--responsive-axis-min), 14px);
717
+ --dpi-factor: 1;
718
+ --aspect-factor: 1;
719
+ --responsive-standard: calc(
720
+ var(--responsive-size) * var(--dpi-factor) * var(--aspect-factor)
721
+ );
722
+ font-size: var(--responsive-standard);
723
+ }
724
+ @media (min-resolution: 1.25dppx) and (max-resolution: 1.49dppx) {
725
+ :root {
726
+ --dpi-factor: 0.96;
727
+ }
728
+ }
729
+ @media (min-resolution: 1.5dppx) and (max-resolution: 1.74dppx) {
730
+ :root {
731
+ --dpi-factor: 0.92;
732
+ }
733
+ }
734
+ @media (min-width: 2560px) and (min-height: 1440px) {
735
+ :root {
736
+ --responsive-size: clamp(9px, var(--responsive-axis-min), 15px);
737
+ }
738
+ }
739
+ @media (min-width: 3200px) and (min-height: 1800px) {
740
+ :root {
741
+ --responsive-size: clamp(10px, var(--responsive-axis-min), 15.5px);
742
+ }
743
+ }
744
+ @media (min-width: 3840px) and (min-height: 2160px) {
745
+ :root {
746
+ --responsive-size: clamp(11px, var(--responsive-axis-min), 16px);
747
+ }
748
+ }
749
+ @media (min-aspect-ratio: 21/9) and (min-width: 2560px) {
750
+ :root {
751
+ --aspect-factor: 1.04;
752
+ }
753
+ }
754
+ @media (max-aspect-ratio: 16/10) and (max-width: 1920px) {
755
+ :root {
756
+ --aspect-factor: 0.98;
757
+ }
715
758
  }
716
759
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniai-fe/uds-foundation",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "UNIAI Design System; Design Foundation Package",
5
5
  "type": "module",
6
6
  "private": false,
@@ -79,7 +79,10 @@
79
79
  "import": "./dist/types/theme-tokens.js",
80
80
  "default": "./dist/types/theme-tokens.js"
81
81
  },
82
- "./css": "./dist/index.css"
82
+ "./css": "./dist/index.css",
83
+ "./scss": "./css.scss",
84
+ "./scss/functions": "./src/scss/functions.scss",
85
+ "./scss/responsive": "./src/scss/responsive.scss"
83
86
  },
84
87
  "scripts": {
85
88
  "format": "prettier --write .",
package/src/index.scss CHANGED
@@ -7,6 +7,7 @@
7
7
  @use "./layout/breakpoint.scss" as layoutBreakpoint;
8
8
  @use "./layout/size.scss" as layoutSize;
9
9
  @use "./layout/radius.scss" as layoutRadius;
10
+ @use "./scss/responsive.scss" as responsiveScale;
10
11
  @use "./typography/fonts/font-face.scss" as typoFontFace;
11
12
  @use "./typography/fonts/font-family.scss" as typoFontFamily;
12
13
  @use "./typography/styles/display.scss" as typoDisplay;
@@ -37,5 +38,9 @@
37
38
  @include typoBody.foundation-typography-body();
38
39
  @include typoLabel.foundation-typography-label();
39
40
  @include typoCaption.foundation-typography-caption();
41
+ @include responsiveScale.base();
40
42
  }
43
+
44
+ // 반응형 rem 보정은 단일 믹스인으로 media query를 출력한다.
45
+ @include responsiveScale.queries();
41
46
  }
@@ -17,7 +17,7 @@
17
17
  }
18
18
 
19
19
  * {
20
- letter-spacing: -0.05em;
20
+ letter-spacing: -0.02em;
21
21
  flex-shrink: 0;
22
22
  overscroll-behavior-y: none;
23
23
  font-family: var(
@@ -0,0 +1,161 @@
1
+ @use "sass:list";
2
+ @use "sass:math";
3
+ @use "sass:string";
4
+
5
+ // CSS-in-JS 스타일링 도구(styleRem/styleBaseSize/styleSpacingSize)를 Sass로 옮긴다.
6
+ // - styleRemAmount, styleBaseSize, styleSpacingSize와 동일한 계산 규칙을 유지한다.
7
+ // - @use alias(`as style`)를 붙이면 style.base-size(...)처럼 짧게 호출할 수 있다.
8
+ $style-rem-amount: 10 !default;
9
+
10
+ // rem: px 수치를 rem으로 환산한다.
11
+ // - module.ts styleRem과 동일하게 10px 기준 단위를 사용한다.
12
+ // - 단위가 이미 붙어 있는 값은 그대로 반환해 중복 환산을 방지한다.
13
+ @function rem($value) {
14
+ @if type-of($value) != "number" {
15
+ // number가 아니면 CSS-in-JS 버전과 동일하게 경고 후 원본을 돌려준다.
16
+ @warn "rem: number만 전달할 수 있습니다.";
17
+ @return $value;
18
+ }
19
+
20
+ @if not math.is-unitless($value) {
21
+ // px 같은 단위가 이미 붙어 있으면 그대로 사용한다.
22
+ @return $value;
23
+ }
24
+
25
+ // 10px 기준 rem 길이를 계산한다.
26
+ @return math.div($value, $style-rem-amount) * 1rem;
27
+ }
28
+
29
+ // base-size: px/rem/string 값을 유효한 CSS 길이로 변환한다.
30
+ // - styleBaseSize의 unit/number/string/alt 분기와 동일하게 동작한다.
31
+ // - alt까지 유효 값이 없으면 auto를 반환하고, 허용 단위(px/rem)가 아니면 px로 강제한다.
32
+ @function base-size($unit, $value, $alt: null) {
33
+ $unit: string.to-lower-case(string.unquote(#{$unit}));
34
+
35
+ @if $unit != "px" and $unit != "rem" {
36
+ // 허용되지 않은 단위가 들어오면 px 강제 후 동일 경고를 남긴다.
37
+ @warn "base-size: #{$unit}는 px/rem만 허용되어 px로 대체합니다.";
38
+ $unit: "px";
39
+ }
40
+
41
+ @if type-of($value) == "number" {
42
+ // px 단위가 붙은 number면 그대로 통과한다.
43
+ @if math.is-unitless($value) == false {
44
+ @return $value;
45
+ }
46
+
47
+ // rem을 요청한 경우 rem 함수를 통해 환산한다.
48
+ @if $unit == "rem" {
49
+ @return rem($value);
50
+ }
51
+
52
+ // px 요청이면 px 단위를 붙인다.
53
+ @return $value * 1px;
54
+ }
55
+
56
+ // 빈 문자열이 아닌 string이면 그대로 사용한다.
57
+ @if type-of($value) == "string" and string.length($value) > 0 {
58
+ @return $value;
59
+ }
60
+
61
+ // alt 값이 설정돼 있으면 동일 규칙으로 재귀 호출한다.
62
+ @if $alt != null {
63
+ @return base-size($unit, $alt);
64
+ }
65
+
66
+ // 값이 완전히 비어 있으면 auto를 기본값으로 사용한다.
67
+ @if $value == null {
68
+ @return auto;
69
+ }
70
+
71
+ // 마지막으로 남은 경우 그대로 문자열로 되돌린다.
72
+ @return $value;
73
+ }
74
+
75
+ // spacing-array: padding/margin 리스트를 공백 문자열로 결합한다.
76
+ // - styleSpacingArray 문서의 파라미터 패턴[상하좌우/상하,좌우/... ]과 동일한 순서를 유지한다.
77
+ @function spacing-array($unit, $values) {
78
+ @if type-of($values) != "list" {
79
+ // 리스트가 아니면 단일 값으로 간주한다.
80
+ @return base-size($unit, $values);
81
+ }
82
+
83
+ $result: ();
84
+
85
+ @for $index from 1 through list.length($values) {
86
+ $entry: list.nth($values, $index);
87
+ // 각 항목을 base-size로 정규화한 뒤 공백으로 결합한다.
88
+ $result: list.append($result, base-size($unit, $entry), "space");
89
+ }
90
+
91
+ @return $result;
92
+ }
93
+
94
+ // spacing-size: padding/margin 입력을 CSS 길이 값으로 정규화한다.
95
+ // - styleSpacingSize 분기 그대로: undefined→alt/0, number|string→base-size, array→spacing-array.
96
+ @function spacing-size($unit, $spacing: null, $alt: null) {
97
+ @if $spacing == null {
98
+ @if $alt == null {
99
+ // spacing/alt 모두 없으면 0을 기본값으로 사용한다.
100
+ @return 0;
101
+ }
102
+
103
+ @if type-of($alt) == "list" {
104
+ // alt가 리스트인 경우 spacing-array 규칙을 그대로 적용한다.
105
+ @return spacing-array($unit, $alt);
106
+ }
107
+
108
+ // alt가 number|string이면 base-size로 정규화한다.
109
+ @return base-size($unit, $alt);
110
+ }
111
+
112
+ @if type-of($spacing) ==
113
+ "number" or
114
+ (type-of($spacing) == "string" and string.length($spacing) > 0)
115
+ {
116
+ // number/string 입력은 base-size에서 처리한다.
117
+ @return base-size($unit, $spacing);
118
+ }
119
+
120
+ @if type-of($spacing) == "list" {
121
+ // 리스트 입력은 spacing-array로 전달한다.
122
+ @return spacing-array($unit, $spacing);
123
+ }
124
+
125
+ // 배열/문자열/숫자 외 타입은 문자열로 강제한다.
126
+ @return $spacing;
127
+ }
128
+
129
+ // padding-size: spacing-size를 감싸 padding 맥락을 명확히 한다.
130
+ @function padding-size($unit, $padding: null, $alt: null) {
131
+ @return spacing-size($unit, $padding, $alt);
132
+ }
133
+
134
+ // margin-size: spacing-size를 감싸 margin 맥락을 명확히 한다.
135
+ @function margin-size($unit, $margin: null, $alt: null) {
136
+ @return spacing-size($unit, $margin, $alt);
137
+ }
138
+
139
+ // spacing: padding/margin 선언 반복을 줄이는 믹스인.
140
+ // - property 파라미터로 padding/margin 외 확장도 가능하다.
141
+ // - $important 인자로 !important 여부를 제어한다.
142
+ @mixin spacing($property, $unit, $value: null, $alt: null, $important: false) {
143
+ $result: spacing-size($unit, $value, $alt);
144
+
145
+ @if $important {
146
+ // !important 플래그가 true인 경우.
147
+ #{$property}: #{$result} !important;
148
+ } @else {
149
+ #{$property}: #{$result};
150
+ }
151
+ }
152
+
153
+ // padding: spacing 믹스인을 padding 속성으로 고정한 래퍼.
154
+ @mixin padding($unit, $value: null, $alt: null, $important: false) {
155
+ @include spacing(padding, $unit, $value, $alt, $important);
156
+ }
157
+
158
+ // margin: spacing 믹스인을 margin 속성으로 고정한 래퍼.
159
+ @mixin margin($unit, $value: null, $alt: null, $important: false) {
160
+ @include spacing(margin, $unit, $value, $alt, $important);
161
+ }
@@ -0,0 +1,91 @@
1
+ // 반응형 폰트 사이즈
2
+ // - 기준 해상도: 1920x1080 (FHD) → 10px 고정
3
+ // - FHD 미만: 높이/너비 중 더 작은 축을 기준으로 축소
4
+ // - FHD 초과: 너무 작아 보이지 않도록 완만하게 확대 (상한 적용)
5
+ // - Windows 노트북(디스플레이 배율 125%/150%)에서 과대 확대를 보정하기 위한 DPI 계수 적용
6
+
7
+ // base: :root 안에서 responsive rem 기본 변수를 정의한다(ui-legacy responsive.scss와 동일 로직).
8
+ @mixin base() {
9
+ // 10px @ 1920x1080 기준 스케일
10
+ // 너비 기준: 10px = (10 / 1920) * 100vw = 0.520833vw
11
+ // 높이 기준: 10px = (10 / 1080) * 100vh = 0.925926vh
12
+ // 더 작은 축을 기준으로 스케일(노트북 저해상도/낮은 세로 해상도 환경 보정)
13
+ --responsive-axis-min: min(0.520833vw, 0.925926vh);
14
+
15
+ // 기본 크기 범위(하한/상한): FHD 미만에서 너무 커지는 문제 방지(하한 ↓),
16
+ // QHD 이상에서 너무 작아 보이는 문제 완화(상한 ↑). 필요 시 수치 미세 조정.
17
+ --responsive-size: clamp(7px, var(--responsive-axis-min), 14px);
18
+
19
+ // DPI 보정 계수: Windows + 125%/150% 배율에서 커지는 현상 완화용
20
+ // 기본 1.0 → 높은 배율에서 소폭 감소
21
+ --dpi-factor: 1;
22
+
23
+ // 화면 비율 보정 계수(울트라와이드/세로 확장형 등)
24
+ --aspect-factor: 1;
25
+
26
+ // 최종 표준 값: 설계상 기준 rem(=10px @ FHD)을 유지하되, 해상도/배율 보정 적용
27
+ --responsive-standard: calc(
28
+ var(--responsive-size) * var(--dpi-factor) * var(--aspect-factor)
29
+ );
30
+
31
+ font-size: var(--responsive-standard);
32
+ }
33
+
34
+ // queries: ui-legacy responsive.scss의 media 규칙을 그대로 내보낸다.
35
+ @mixin queries() {
36
+ // 고배율(배율 125% 이상 추정) 보정: 소폭 축소
37
+ // DPI 보정: Mac Retina(2dppx)에는 영향이 없도록 범위를 제한
38
+ @media (min-resolution: 1.25dppx) and (max-resolution: 1.49dppx) {
39
+ :root {
40
+ --dpi-factor: 0.96;
41
+ }
42
+ }
43
+
44
+ @media (min-resolution: 1.5dppx) and (max-resolution: 1.74dppx) {
45
+ :root {
46
+ --dpi-factor: 0.92;
47
+ }
48
+ }
49
+
50
+ // QHD 이상에서 상한을 점진적으로 상향해 4K와의 구분감 확보
51
+ @media (min-width: 2560px) and (min-height: 1440px) {
52
+ :root {
53
+ --responsive-size: clamp(9px, var(--responsive-axis-min), 15px);
54
+ }
55
+ }
56
+
57
+ @media (min-width: 3200px) and (min-height: 1800px) {
58
+ :root {
59
+ --responsive-size: clamp(10px, var(--responsive-axis-min), 15.5px);
60
+ }
61
+ }
62
+
63
+ @media (min-width: 3840px) and (min-height: 2160px) {
64
+ :root {
65
+ --responsive-size: clamp(11px, var(--responsive-axis-min), 16px);
66
+ }
67
+ }
68
+
69
+ // 울트라와이드(21:9 이상)에서 UI가 상대적으로 작아보이는 현상 완화
70
+ @media (min-aspect-ratio: 21 / 9) and (min-width: 2560px) {
71
+ :root {
72
+ --aspect-factor: 1.04;
73
+ }
74
+ }
75
+
76
+ // 세로 확장형(16:10 이하)에서 과도한 확대 방지
77
+ @media (max-aspect-ratio: 16 / 10) and (max-width: 1920px) {
78
+ :root {
79
+ --aspect-factor: 0.98;
80
+ }
81
+ }
82
+ }
83
+
84
+ // responsive: base + queries를 한 번에 출력하는 편의 믹스인.
85
+ @mixin responsive() {
86
+ :root {
87
+ @include base();
88
+ }
89
+
90
+ @include queries();
91
+ }