@madeinbruno/atelier 0.0.0-stage → 0.2.0
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 +56 -2
- package/dist/index.d.ts +30 -0
- package/dist/index.js +48 -0
- package/package.json +46 -4
- package/styles.css +186 -0
package/README.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @madeinbruno/atelier
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
madeinbruno.com의 서비스들이 같이 쓰는 공용 컴포넌트예요. 옷 만드는 작업실(아틀리에) 컨셉이라 컴포넌트도 택, 라벨, 도장처럼 옷에 붙는 것들로 지어요.
|
|
4
|
+
|
|
5
|
+
## 설치
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @madeinbruno/atelier
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
React 18 이상이 필요해요.
|
|
12
|
+
|
|
13
|
+
## 컴포넌트
|
|
14
|
+
|
|
15
|
+
### `Tag` — 공용 푸터
|
|
16
|
+
|
|
17
|
+
흰 옷 택(행택) 모양의 푸터예요. 택 위쪽에 한 줄 박음질이 있고, 옷 택처럼 `MADE IN BRUNO / STYLE / SIZE / CONTACT`와 madeinbruno.com 링크가 들어가요.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { Tag } from '@madeinbruno/atelier';
|
|
21
|
+
import '@madeinbruno/atelier/styles.css'; // 앱에서 한 번만
|
|
22
|
+
|
|
23
|
+
export default function Footer() {
|
|
24
|
+
return <Tag service="hangoutwithme" background="#d6efdf" />;
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
| prop | 기본값 | 설명 |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `service` (필수) | | 택의 STYLE 줄. 서비스 이름 |
|
|
31
|
+
| `email` | `pbk575@gmail.com` | CONTACT 줄 (mailto 링크) |
|
|
32
|
+
| `href` | `https://madeinbruno.com` | 택 아래 링크 (밑줄 친 주소) |
|
|
33
|
+
| `since` | `EST. 2026` | 택 오른쪽 위 작은 글씨 |
|
|
34
|
+
| `eyebrow`, `message` | "Thanks for stopping by", "이 사이트도 madeinbruno에서 만들었어요." | 택 왼쪽 글. 둘 다 비우면 택만 가운데에 |
|
|
35
|
+
| `background` | `#f4f1ea` | 푸터 바탕. 서비스 바탕색에 맞추기 |
|
|
36
|
+
| `stitch` | `#9a948b` (회색) | 박음질 색 |
|
|
37
|
+
|
|
38
|
+
색은 CSS 변수(`--mib-ground`, `--mib-stitch`, `--mib-ink`)로도 바꿀 수 있어요. 클래스는 모두 `mib-`로 시작해 서비스 스타일과 겹치지 않아요. 글꼴(Archivo, IBM Plex Mono)은 `styles.css`가 Google Fonts에서 불러와요.
|
|
39
|
+
|
|
40
|
+
## 개발
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pnpm --filter @madeinbruno/atelier build # dist/ 생성
|
|
44
|
+
pnpm --filter @madeinbruno/atelier dev # 고칠 때마다 다시 빌드
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 배포 (npm)
|
|
48
|
+
|
|
49
|
+
GitHub Actions(`.github/workflows/publish-atelier.yml`)가 배포해요.
|
|
50
|
+
|
|
51
|
+
1. 고친 뒤 `package.json`의 `version`을 올려서 `main`에 푸시해요 (버전을 안 올리면 건너뛰어요).
|
|
52
|
+
2. Actions가 빌드해서 npm 승인 대기열(staged)에 올려요.
|
|
53
|
+
3. npmjs.com에서 그 버전을 승인(2단계 인증)하면 공개돼요.
|
|
54
|
+
|
|
55
|
+
npm 로그인은 trusted publishing(OIDC)이라 저장소에 토큰이 없어요. 처음 한 번, npmjs.com의 패키지 설정 → Trusted publishing에 GitHub 저장소 `codingbotPark/madeinbruno`, 워크플로 파일 `publish-atelier.yml`을 등록해 둬야 해요.
|
|
56
|
+
|
|
57
|
+
손으로 올릴 때는 `npm login` 후 `packages/atelier`에서 `npm stage publish --access public`.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type TagProps = {
|
|
5
|
+
/** 이 서비스의 이름. 택의 STYLE 줄에 들어간다 (예: "hangoutwithme"). */
|
|
6
|
+
service: string;
|
|
7
|
+
/** 연락받을 이메일. 택의 CONTACT 줄에 mailto 링크로 들어간다. */
|
|
8
|
+
email?: string;
|
|
9
|
+
/** 택 아래 링크(밑줄 친 주소)가 가는 곳. */
|
|
10
|
+
href?: string;
|
|
11
|
+
/** 택 오른쪽 위의 작은 글씨. */
|
|
12
|
+
since?: string;
|
|
13
|
+
/** 택 왼쪽에 놓이는 글. 작은 머리글(eyebrow)과 한두 줄 본문. 비우면 택만 가운데에 놓인다. */
|
|
14
|
+
eyebrow?: ReactNode;
|
|
15
|
+
message?: ReactNode;
|
|
16
|
+
/** 푸터 바탕색. 서비스 바탕에 맞춘다 (기본: 옅은 크림). */
|
|
17
|
+
background?: string;
|
|
18
|
+
/** 택 위쪽 박음질 색 (기본: 회색). */
|
|
19
|
+
stitch?: string;
|
|
20
|
+
className?: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* madeinbruno 공용 푸터: 흰 옷 택(행택).
|
|
24
|
+
* 택 위쪽에 한 줄 박음질, 모서리를 깎은 모양이고, 옷 택 형식으로
|
|
25
|
+
* MADE IN BRUNO / STYLE(서비스 이름) / SIZE / CONTACT(이메일)와 madeinbruno.com 링크를 담는다.
|
|
26
|
+
* 스타일은 '@madeinbruno/atelier/styles.css'를 한 번 불러와야 적용된다.
|
|
27
|
+
*/
|
|
28
|
+
declare function Tag({ service, email, href, since, eyebrow, message, background, stitch, className, }: TagProps): react.JSX.Element;
|
|
29
|
+
|
|
30
|
+
export { Tag, type TagProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/Tag.tsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function Tag({
|
|
4
|
+
service,
|
|
5
|
+
email = "pbk575@gmail.com",
|
|
6
|
+
href = "https://madeinbruno.com",
|
|
7
|
+
since = "EST. 2026",
|
|
8
|
+
eyebrow = "Thanks for stopping by",
|
|
9
|
+
message = `\uC774 \uC0AC\uC774\uD2B8\uB3C4 madeinbruno\uC5D0\uC11C \uB9CC\uB4E4\uC5C8\uC5B4\uC694.`,
|
|
10
|
+
background,
|
|
11
|
+
stitch,
|
|
12
|
+
className
|
|
13
|
+
}) {
|
|
14
|
+
const style = {
|
|
15
|
+
...background ? { "--mib-ground": background } : {},
|
|
16
|
+
...stitch ? { "--mib-stitch": stitch } : {}
|
|
17
|
+
};
|
|
18
|
+
const hasMessage = Boolean(eyebrow || message);
|
|
19
|
+
const linkLabel = href.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
20
|
+
return /* @__PURE__ */ jsx("footer", { className: ["mib-footer", className].filter(Boolean).join(" "), style, children: /* @__PURE__ */ jsxs("div", { className: hasMessage ? "mib-footer__inner" : "mib-footer__inner mib-footer__inner--solo", children: [
|
|
21
|
+
hasMessage && /* @__PURE__ */ jsxs("div", { className: "mib-footer__message", children: [
|
|
22
|
+
eyebrow && /* @__PURE__ */ jsx("p", { className: "mib-footer__eyebrow", children: eyebrow }),
|
|
23
|
+
message && /* @__PURE__ */ jsx("p", { className: "mib-footer__text", children: message })
|
|
24
|
+
] }),
|
|
25
|
+
/* @__PURE__ */ jsx("div", { className: "mib-tag-hang", children: /* @__PURE__ */ jsx("div", { className: "mib-tag", children: /* @__PURE__ */ jsxs("div", { className: "mib-tag__card", children: [
|
|
26
|
+
/* @__PURE__ */ jsx("span", { className: "mib-tag__stitch", "aria-hidden": "true" }),
|
|
27
|
+
/* @__PURE__ */ jsxs("div", { className: "mib-tag__head", children: [
|
|
28
|
+
/* @__PURE__ */ jsx("span", { className: "mib-tag__brand", children: "MADE IN BRUNO" }),
|
|
29
|
+
/* @__PURE__ */ jsx("span", { className: "mib-tag__since", children: since })
|
|
30
|
+
] }),
|
|
31
|
+
/* @__PURE__ */ jsxs("dl", { className: "mib-tag__specs", children: [
|
|
32
|
+
/* @__PURE__ */ jsx("dt", { children: "STYLE" }),
|
|
33
|
+
/* @__PURE__ */ jsx("dd", { children: service }),
|
|
34
|
+
/* @__PURE__ */ jsx("dt", { children: "SIZE" }),
|
|
35
|
+
/* @__PURE__ */ jsx("dd", { children: "ONE OF ONE" }),
|
|
36
|
+
/* @__PURE__ */ jsx("dt", { children: "CONTACT" }),
|
|
37
|
+
/* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsx("a", { href: `mailto:${email}`, children: email }) })
|
|
38
|
+
] }),
|
|
39
|
+
/* @__PURE__ */ jsxs("a", { className: "mib-tag__link", href, children: [
|
|
40
|
+
linkLabel,
|
|
41
|
+
" \u2192"
|
|
42
|
+
] })
|
|
43
|
+
] }) }) })
|
|
44
|
+
] }) });
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
Tag
|
|
48
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@madeinbruno/atelier",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "madeinbruno.com 서비스들이 같이 쓰는 공용 컴포넌트 (옷 만드는 작업실 컨셉: 택, 라벨, 도장…)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "codingbotPark <pbk575@gmail.com>",
|
|
7
|
+
"homepage": "https://madeinbruno.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/codingbotPark/madeinbruno.git",
|
|
11
|
+
"directory": "packages/atelier"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"sideEffects": [
|
|
15
|
+
"*.css"
|
|
16
|
+
],
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./styles.css": "./styles.css"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"styles.css"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsup",
|
|
32
|
+
"dev": "tsup --watch",
|
|
33
|
+
"check": "tsc --noEmit",
|
|
34
|
+
"prepublishOnly": "pnpm build"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"react": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/react": "^19",
|
|
41
|
+
"react": "19.2.3",
|
|
42
|
+
"tsup": "^8.5.0",
|
|
43
|
+
"typescript": "^5"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
}
|
|
48
|
+
}
|
package/styles.css
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/* @madeinbruno/atelier 공용 스타일. 쓰는 쪽에서 한 번만 불러온다: import '@madeinbruno/atelier/styles.css'
|
|
2
|
+
클래스 이름은 모두 mib- 로 시작해 서비스 자체 스타일과 겹치지 않는다.
|
|
3
|
+
색은 CSS 변수로 바꿀 수 있다: --mib-ground(푸터 바탕), --mib-stitch(박음질), --mib-ink(글씨). */
|
|
4
|
+
@import url('https://fonts.googleapis.com/css2?family=Archivo:wght@500;800&family=IBM+Plex+Mono:wght@400;500&display=swap');
|
|
5
|
+
|
|
6
|
+
/* ───────── Tag: 흰 옷 택 푸터 ───────── */
|
|
7
|
+
|
|
8
|
+
.mib-footer {
|
|
9
|
+
--mib-ground: #f4f1ea;
|
|
10
|
+
--mib-stitch: #9a948b;
|
|
11
|
+
--mib-ink: #1f1d1a;
|
|
12
|
+
--mib-muted: #6b655c;
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
width: 100%;
|
|
15
|
+
overflow: hidden;
|
|
16
|
+
padding: 0 24px;
|
|
17
|
+
border-top: 1px solid rgba(31, 29, 26, 0.12);
|
|
18
|
+
background: var(--mib-ground);
|
|
19
|
+
font-family: 'IBM Plex Mono', ui-monospace, monospace;
|
|
20
|
+
color: var(--mib-ink);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.mib-footer *,
|
|
24
|
+
.mib-footer *::before,
|
|
25
|
+
.mib-footer *::after {
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.mib-footer a {
|
|
30
|
+
color: inherit;
|
|
31
|
+
text-decoration: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.mib-footer a:hover {
|
|
35
|
+
color: #b4442f;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.mib-footer__inner {
|
|
39
|
+
display: flex;
|
|
40
|
+
flex-wrap: wrap;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: space-between;
|
|
43
|
+
gap: 32px 64px;
|
|
44
|
+
max-width: 1120px;
|
|
45
|
+
margin: 0 auto;
|
|
46
|
+
padding-bottom: 56px;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.mib-footer__inner--solo {
|
|
50
|
+
justify-content: center;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.mib-footer__message {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex: 1 1 300px;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
gap: 14px;
|
|
58
|
+
max-width: 440px;
|
|
59
|
+
padding-top: 64px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.mib-footer__eyebrow {
|
|
63
|
+
margin: 0;
|
|
64
|
+
font-size: 13px;
|
|
65
|
+
letter-spacing: 0.08em;
|
|
66
|
+
text-transform: uppercase;
|
|
67
|
+
color: var(--mib-muted);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.mib-footer__text {
|
|
71
|
+
margin: 0;
|
|
72
|
+
font-family: 'Archivo', system-ui, sans-serif;
|
|
73
|
+
font-size: clamp(20px, 3vw, 28px);
|
|
74
|
+
font-weight: 500;
|
|
75
|
+
line-height: 1.35;
|
|
76
|
+
letter-spacing: -0.01em;
|
|
77
|
+
word-break: keep-all;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.mib-tag-hang {
|
|
81
|
+
position: relative;
|
|
82
|
+
flex: 0 1 470px;
|
|
83
|
+
padding-top: 56px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.mib-tag {
|
|
87
|
+
filter: drop-shadow(0 10px 18px rgba(31, 29, 26, 0.14));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.mib-tag__card {
|
|
91
|
+
position: relative;
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
gap: 14px;
|
|
95
|
+
min-height: 236px;
|
|
96
|
+
padding: 46px 34px 26px;
|
|
97
|
+
background: #ffffff;
|
|
98
|
+
/* 옷 택처럼 위쪽 두 모서리를 비스듬히 깎는다 */
|
|
99
|
+
clip-path: polygon(18px 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 0 100%, 0 18px);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.mib-tag__stitch {
|
|
103
|
+
position: absolute;
|
|
104
|
+
top: 22px;
|
|
105
|
+
left: 22px;
|
|
106
|
+
right: 22px;
|
|
107
|
+
height: 2px;
|
|
108
|
+
background: repeating-linear-gradient(90deg, var(--mib-stitch) 0 9px, transparent 9px 15px);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.mib-tag__head {
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-wrap: wrap;
|
|
114
|
+
align-items: baseline;
|
|
115
|
+
justify-content: space-between;
|
|
116
|
+
gap: 4px 12px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.mib-tag__brand {
|
|
120
|
+
font-family: 'Archivo', system-ui, sans-serif;
|
|
121
|
+
font-size: clamp(24px, 7vw, 31px);
|
|
122
|
+
font-weight: 800;
|
|
123
|
+
letter-spacing: 0.02em;
|
|
124
|
+
white-space: nowrap;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.mib-tag__since {
|
|
128
|
+
font-size: 12px;
|
|
129
|
+
color: var(--mib-muted);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.mib-tag__specs {
|
|
133
|
+
display: grid;
|
|
134
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
135
|
+
gap: 6px 24px;
|
|
136
|
+
margin: 0;
|
|
137
|
+
padding-top: 14px;
|
|
138
|
+
border-top: 1px solid #e7e1d6;
|
|
139
|
+
font-size: 13px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.mib-tag__specs dt {
|
|
143
|
+
color: var(--mib-muted);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.mib-tag__specs dd {
|
|
147
|
+
margin: 0;
|
|
148
|
+
overflow-wrap: anywhere;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.mib-tag__link {
|
|
152
|
+
align-self: flex-start;
|
|
153
|
+
margin-top: auto;
|
|
154
|
+
padding-bottom: 2px;
|
|
155
|
+
border-bottom: 1px solid currentColor;
|
|
156
|
+
font-size: 14px;
|
|
157
|
+
font-weight: 500;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* 좁은 화면: 글 아래에 택이 오고, 택은 화면 폭에 맞춘다. */
|
|
161
|
+
@media (max-width: 640px) {
|
|
162
|
+
.mib-footer__inner {
|
|
163
|
+
flex-direction: column;
|
|
164
|
+
align-items: stretch;
|
|
165
|
+
gap: 8px;
|
|
166
|
+
padding-bottom: 40px;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.mib-footer__message {
|
|
170
|
+
flex: none;
|
|
171
|
+
padding-top: 48px;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.mib-tag-hang {
|
|
175
|
+
flex: none;
|
|
176
|
+
width: 100%;
|
|
177
|
+
max-width: 470px;
|
|
178
|
+
align-self: center;
|
|
179
|
+
padding-top: 24px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.mib-tag__card {
|
|
183
|
+
min-height: 0;
|
|
184
|
+
padding: 44px 24px 24px;
|
|
185
|
+
}
|
|
186
|
+
}
|