@hjmds/design-contracts 1.37.2 → 1.38.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/dist/card.d.ts +2 -1
- package/dist/card.d.ts.map +1 -1
- package/dist/card.js +3 -1
- package/dist/card.js.map +1 -1
- package/dist/catalog.d.ts +42 -12
- package/dist/catalog.d.ts.map +1 -1
- package/dist/colors.js +2 -2
- package/dist/colors.js.map +1 -1
- package/dist/component-recipes.d.ts +34 -6
- package/dist/component-recipes.d.ts.map +1 -1
- package/dist/component-recipes.js +16 -9
- package/dist/component-recipes.js.map +1 -1
- package/dist/result.d.ts +1 -1
- package/dist/result.d.ts.map +1 -1
- package/dist/result.js +2 -1
- package/dist/result.js.map +1 -1
- package/dist/screen-patterns.d.ts +3 -3
- package/dist/screen-patterns.d.ts.map +1 -1
- package/dist/screen-patterns.js +5 -4
- package/dist/screen-patterns.js.map +1 -1
- package/dist/semantic-colors.d.ts +10 -0
- package/dist/semantic-colors.d.ts.map +1 -1
- package/dist/semantic-colors.js +3 -0
- package/dist/semantic-colors.js.map +1 -1
- package/dist/steps.d.ts +2 -1
- package/dist/steps.d.ts.map +1 -1
- package/dist/steps.js +3 -1
- package/dist/steps.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/docs/collection-rail.md +3 -0
- package/docs/generated/component-maturity.md +1 -1
- package/docs/generated/renderer-evidence.json +3 -3
- package/docs/generated/renderer-evidence.md +1 -1
- package/docs/generated/showcase-manifest.json +1 -1
- package/docs/theme-palette.md +7 -0
- package/docs/usage/README.md +22 -22
- package/docs/usage/SELECTION.md +2 -2
- package/docs/usage/components/alert-dialog.md +1 -1
- package/docs/usage/components/auth-provider-button.md +1 -1
- package/docs/usage/components/auth-screen-layout.md +4 -2
- package/docs/usage/components/bottom-cta.md +1 -1
- package/docs/usage/components/button.md +7 -1
- package/docs/usage/components/card.md +2 -1
- package/docs/usage/components/chat-message.md +2 -2
- package/docs/usage/components/chat-screen.md +2 -2
- package/docs/usage/components/comment-thread-row.md +11 -7
- package/docs/usage/components/comment-thread-screen.md +3 -3
- package/docs/usage/components/composition-layout.md +6 -2
- package/docs/usage/components/conversion-funnel.md +3 -3
- package/docs/usage/components/design-system-provider.md +1 -1
- package/docs/usage/components/dialog.md +2 -2
- package/docs/usage/components/editor-screen.md +2 -2
- package/docs/usage/components/form.md +1 -1
- package/docs/usage/components/icon-button.md +6 -0
- package/docs/usage/components/image-choice-group.md +2 -2
- package/docs/usage/components/list-detail-screen.md +2 -2
- package/docs/usage/components/list.md +1 -0
- package/docs/usage/components/media-selection-screen.md +3 -3
- package/docs/usage/components/message-composer.md +2 -2
- package/docs/usage/components/moderation-screen.md +2 -2
- package/docs/usage/components/notice.md +1 -1
- package/docs/usage/components/notification-inbox-screen.md +2 -2
- package/docs/usage/components/notification-item.md +2 -2
- package/docs/usage/components/onboarding-screen.md +2 -2
- package/docs/usage/components/overview-screen.md +2 -2
- package/docs/usage/components/permission-screen.md +2 -2
- package/docs/usage/components/photo-source-sheet.md +1 -1
- package/docs/usage/components/profile-screen.md +2 -2
- package/docs/usage/components/profile-summary.md +1 -1
- package/docs/usage/components/result.md +2 -2
- package/docs/usage/components/screen-layout.md +3 -3
- package/docs/usage/components/section.md +1 -1
- package/docs/usage/components/segmented-control.md +4 -0
- package/docs/usage/components/selection-action-bar.md +1 -1
- package/docs/usage/components/settings-screen.md +2 -2
- package/docs/usage/components/sheet.md +1 -1
- package/docs/usage/components/shortcut-recorder.md +1 -1
- package/docs/usage/components/side-panel.md +1 -1
- package/docs/usage/components/statistic.md +1 -1
- package/docs/usage/components/steps.md +4 -2
- package/docs/usage/components/streaming-answer.md +1 -1
- package/docs/usage/components/table-column-settings.md +1 -1
- package/docs/usage/components/tabs.md +4 -0
- package/docs/usage/components/top-bar.md +10 -4
- package/docs/usage/components/transfer-list.md +1 -1
- package/docs/usage/components/transfer-task.md +1 -1
- package/docs/usage/compositions/compound-controls.md +1 -1
- package/docs/usage/compositions/expo-interactions.md +1 -1
- package/docs/usage/compositions/input-sheet.md +1 -1
- package/docs/usage/compositions/interaction-flow-apply.md +4 -4
- package/docs/usage/compositions/interaction-flow-draft.md +3 -3
- package/docs/usage/compositions/paper-surface.md +1 -1
- package/docs/usage/screens/flow-media.md +1 -1
- package/docs/usage/screens/mockup-studio.md +2 -2
- package/docs/usage/tokens/color.md +8 -2
- package/docs/usage/tokens/spacing.md +2 -2
- package/package.json +1 -1
package/dist/steps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { FontWeightValue, TextVariant } from \"./foundations.js\";\nimport type { SemanticIconName } from \"./icon.js\";\nimport { control, fontWeight, glyph, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Ant Design Steps' full surface (navigation jump, dot type, custom per-item\n * icon, vertical orientation) is intentionally not reproduced. Steps stay a\n * single-cursor horizontal flow. Validated forms default to backward-only\n * navigation; reading flows explicitly opt into free navigation (2026-10-19 portfolio).\n * See docs/steps.md for the per-axis rationale.\n */\nexport type StepStatus = \"pending\" | \"current\" | \"complete\" | \"error\";\n\n/** Only the cursor step can be \"current\" or \"error\"; every other step is derived. */\n// \"complete\" marks a finished flow; see docs/steps.md.\nexport type StepCursorStatus = Extract<StepStatus, \"current\" | \"error\" | \"complete\">;\n\nexport type StepItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n description?: string;\n}>;\n\n/**\n * A single stable cursor (`currentStepId`) is the source of truth instead of a\n * per-item status array, so \"two current steps\" or \"a completed step past an\n * unresolved error\" cannot be represented. Linear navigation only returns before the cursor. Free reading navigation\n * can select any step without changing the single-cursor state model.\n */\nexport type StepsDescriptor<Id extends string = string> = Readonly<{\n steps: readonly StepItemDescriptor<Id>[];\n currentStepId: Id;\n currentStepStatus?: StepCursorStatus;\n /** Free navigation is for reading flows; linear remains the default for validated forms. */\n navigationMode?: \"linear\" | \"free\";\n}>;\n\nexport const stepsDefaults = {\n currentStepStatus: \"current\",\n} as const satisfies Readonly<{ currentStepStatus: StepCursorStatus }>;\n\nexport type StepsStatusLabels = Readonly<{\n pending: string;\n current: string;\n complete: string;\n error: string;\n}>;\n\nexport type StepsAccessibleNameInfo = Readonly<{\n position: number;\n total: number;\n label: string;\n}>;\n\n/**\n * Products own the exact phrase order and counter-word grammar (Korean\n * \"3단계 중 2단계\" does not translate structurally to English \"step 2 of 3\"),\n * so the resolver invokes this composer instead of assembling copy itself.\n */\nexport type ComposeStepsAccessibleName = (\n info: StepsAccessibleNameInfo,\n) => string;\n\nexport type ResolvedStepDescriptor<Id extends string = string> =\n StepItemDescriptor<Id> &\n Readonly<{\n status: StepStatus;\n /** 1-indexed so position and label both preserve reading order for renderers. */\n position: number;\n total: number;\n /** Primary accessible name: order + label. Never says status by itself. */\n accessibleName: string;\n /** Supplementary status announcement (Web aria-describedby text / RN accessibilityHint). */\n statusLabel: string;\n /**\n * Strictly before the cursor. Only these steps become buttons when the product supplies a step press handler:\n * jumping forward would skip validation, and the cursor step is where the user already is (2026-10-18).\n */\n revisitable: boolean;\n }>;\n\nexport type ResolveStepsOptions = Readonly<{\n statusLabels: StepsStatusLabels;\n composeAccessibleName: ComposeStepsAccessibleName;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Steps ${field} must not be empty`);\n }\n}\n\nexport function validateStepItemDescriptor<Id extends string>(\n step: StepItemDescriptor<Id>,\n): void {\n assertNonEmpty(step.id, \"item id\");\n if (step.id !== step.id.trim()) {\n throw new TypeError(\"Steps item id must not start or end with whitespace\");\n }\n assertNonEmpty(step.label, \"item label\");\n if (step.description !== undefined) {\n assertNonEmpty(step.description, \"item description\");\n }\n}\n\nexport function validateStepsDescriptor<Id extends string>(\n descriptor: StepsDescriptor<Id>,\n): void {\n if (descriptor.navigationMode !== undefined && descriptor.navigationMode !== \"linear\" && descriptor.navigationMode !== \"free\") {\n throw new TypeError(`Unsupported Steps navigationMode: ${String(descriptor.navigationMode)}`);\n }\n if (descriptor.steps.length < 2) {\n throw new RangeError(\"Steps must contain at least two steps\");\n }\n const ids = new Set<Id>();\n for (const step of descriptor.steps) {\n validateStepItemDescriptor(step);\n if (ids.has(step.id)) {\n throw new TypeError(`Duplicate Steps item id: ${step.id}`);\n }\n ids.add(step.id);\n }\n if (!ids.has(descriptor.currentStepId)) {\n throw new RangeError(\n `Steps currentStepId does not match any step id: ${String(descriptor.currentStepId)}`,\n );\n }\n if (\n descriptor.currentStepStatus !== undefined &&\n descriptor.currentStepStatus !== \"current\" &&\n descriptor.currentStepStatus !== \"error\" &&\n descriptor.currentStepStatus !== \"complete\"\n ) {\n throw new TypeError(\n `Unsupported Steps currentStepStatus: ${String(descriptor.currentStepStatus)}`,\n );\n }\n}\n\nexport function validateStepsStatusLabels(labels: StepsStatusLabels): void {\n if (labels === null || typeof labels !== \"object\") {\n throw new TypeError(\"Steps statusLabels must be an object\");\n }\n for (const key of [\"pending\", \"current\", \"complete\", \"error\"] as const) {\n const value = labels[key];\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Steps statusLabels.${key} must not be empty`);\n }\n }\n}\n\n/** A step is visually and semantically \"reached\" once it is no longer waiting. */\nexport function isStepReached(status: StepStatus): boolean {\n return status !== \"pending\";\n}\n\n/**\n * Derives every step's status from its position relative to the single\n * cursor, then attaches the order-preserving accessible name and the\n * supplementary status announcement. Throws on a malformed descriptor,\n * labels bag, or composer instead of silently rendering an inconsistent flow.\n */\nexport function resolveStepsDescriptor<Id extends string>(\n descriptor: StepsDescriptor<Id>,\n options: ResolveStepsOptions,\n): readonly ResolvedStepDescriptor<Id>[] {\n validateStepsDescriptor(descriptor);\n validateStepsStatusLabels(options.statusLabels);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Steps composeAccessibleName must be a function\");\n }\n\n const currentStepStatus =\n descriptor.currentStepStatus ?? stepsDefaults.currentStepStatus;\n const total = descriptor.steps.length;\n const cursorIndex = descriptor.steps.findIndex(\n (step) => step.id === descriptor.currentStepId,\n );\n\n return descriptor.steps.map((step, index) => {\n const status: StepStatus =\n index < cursorIndex\n ? \"complete\"\n : index > cursorIndex\n ? \"pending\"\n : currentStepStatus;\n const position = index + 1;\n const accessibleName = options.composeAccessibleName({\n position,\n total,\n label: step.label,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Steps composeAccessibleName must return a non-empty string\",\n );\n }\n return {\n ...step,\n status,\n position,\n total,\n accessibleName,\n statusLabel: options.statusLabels[status],\n revisitable: index < cursorIndex,\n };\n });\n}\n\nexport const stepsRecipe = {\n slots: [\n \"root\",\n \"step\",\n \"indicator\",\n \"marker\",\n \"connector\",\n \"label\",\n \"description\",\n ] as const,\n gap: spacing.xs,\n indicator: {\n size: glyph.md,\n borderWidth: stroke.default,\n activeBorderWidth: stroke.strong,\n /** null means the renderer shows the step's own position number instead of a glyph. */\n marks: {\n pending: null,\n current: null,\n complete: \"check\",\n error: \"error\",\n },\n border: {\n pending: semanticColors.border.default,\n current: semanticColors.content.brand,\n complete: semanticColors.feedback.success.border,\n error: semanticColors.feedback.danger.border,\n },\n background: {\n pending: null,\n current: null,\n complete: semanticColors.feedback.success.badgeBackground,\n error: semanticColors.feedback.danger.badgeBackground,\n },\n content: {\n pending: semanticColors.content.secondary,\n current: semanticColors.content.brand,\n complete: semanticColors.feedback.success.foreground,\n error: semanticColors.feedback.danger.foreground,\n },\n },\n connector: {\n height: stroke.default,\n tone: {\n reached: semanticColors.content.brand,\n unreached: semanticColors.border.default,\n },\n },\n label: {\n textVariant: \"label\",\n fontWeight: fontWeight.medium,\n color: {\n pending: semanticColors.content.secondary,\n current: semanticColors.content.primary,\n complete: semanticColors.content.primary,\n error: semanticColors.feedback.danger.foreground,\n },\n },\n description: {\n textVariant: \"caption\",\n color: semanticColors.content.secondary,\n },\n /** A revisitable step is one button over its marker and label; the shared touch minimum applies to the whole cell. */\n press: {\n minTouchTarget: control.minTouchTarget,\n },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n indicator: {\n size: number;\n borderWidth: number;\n activeBorderWidth: number;\n marks: Record<StepStatus, SemanticIconName | null>;\n border: Record<StepStatus, ColorReference>;\n background: Record<StepStatus, ColorReference | null>;\n content: Record<StepStatus, ColorReference>;\n };\n connector: {\n height: number;\n tone: Record<\"reached\" | \"unreached\", ColorReference>;\n };\n label: {\n textVariant: TextVariant;\n fontWeight: FontWeightValue;\n color: Record<StepStatus, ColorReference>;\n };\n description: {\n textVariant: TextVariant;\n color: ColorReference;\n };\n press: {\n minTouchTarget: number;\n };\n};\n"]}
|
|
1
|
+
{"version":3,"file":"steps.js","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ColorReference } from \"./color-references.js\";\nimport type { FontWeightValue, TextVariant } from \"./foundations.js\";\nimport type { SemanticIconName } from \"./icon.js\";\nimport { control, fontWeight, glyph, spacing, stroke } from \"./foundations.js\";\nimport { semanticColors } from \"./semantic-colors.js\";\n\n/**\n * Ant Design Steps' full surface (navigation jump, dot type, custom per-item\n * icon, vertical orientation) is intentionally not reproduced. Steps stay a\n * single-cursor horizontal flow. Validated forms default to backward-only\n * navigation; reading flows explicitly opt into free navigation (2026-10-19 portfolio).\n * See docs/steps.md for the per-axis rationale.\n */\nexport type StepStatus = \"pending\" | \"current\" | \"complete\" | \"error\";\n\n/** Only the cursor step can be \"current\" or \"error\"; every other step is derived. */\n// \"complete\" marks a finished flow; see docs/steps.md.\nexport type StepCursorStatus = Extract<StepStatus, \"current\" | \"error\" | \"complete\">;\n\nexport type StepItemDescriptor<Id extends string = string> = Readonly<{\n id: Id;\n label: string;\n description?: string;\n}>;\n\n/**\n * A single stable cursor (`currentStepId`) is the source of truth instead of a\n * per-item status array, so \"two current steps\" or \"a completed step past an\n * unresolved error\" cannot be represented. Linear navigation only returns before the cursor. Free reading navigation\n * can select any step without changing the single-cursor state model.\n */\nexport type StepsDescriptor<Id extends string = string> = Readonly<{\n steps: readonly StepItemDescriptor<Id>[];\n currentStepId: Id;\n currentStepStatus?: StepCursorStatus;\n /** Free navigation is for reading flows; linear remains the default for validated forms. */\n navigationMode?: \"linear\" | \"free\";\n}>;\n\nexport const stepsDefaults = {\n currentStepStatus: \"current\",\n} as const satisfies Readonly<{ currentStepStatus: StepCursorStatus }>;\n\nexport type StepsStatusLabels = Readonly<{\n pending: string;\n current: string;\n complete: string;\n error: string;\n}>;\n\nexport type StepsAccessibleNameInfo = Readonly<{\n position: number;\n total: number;\n label: string;\n}>;\n\n/**\n * Products own the exact phrase order and counter-word grammar (Korean\n * \"3단계 중 2단계\" does not translate structurally to English \"step 2 of 3\"),\n * so the resolver invokes this composer instead of assembling copy itself.\n */\nexport type ComposeStepsAccessibleName = (\n info: StepsAccessibleNameInfo,\n) => string;\n\nexport type ResolvedStepDescriptor<Id extends string = string> =\n StepItemDescriptor<Id> &\n Readonly<{\n status: StepStatus;\n /** 1-indexed so position and label both preserve reading order for renderers. */\n position: number;\n total: number;\n /** Primary accessible name: order + label. Never says status by itself. */\n accessibleName: string;\n /** Supplementary status announcement (Web aria-describedby text / RN accessibilityHint). */\n statusLabel: string;\n /**\n * Strictly before the cursor. Only these steps become buttons when the product supplies a step press handler:\n * jumping forward would skip validation, and the cursor step is where the user already is (2026-10-18).\n */\n revisitable: boolean;\n }>;\n\nexport type ResolveStepsOptions = Readonly<{\n statusLabels: StepsStatusLabels;\n composeAccessibleName: ComposeStepsAccessibleName;\n}>;\n\nfunction assertNonEmpty(value: string, field: string): void {\n if (value.trim().length === 0) {\n throw new TypeError(`Steps ${field} must not be empty`);\n }\n}\n\nexport function validateStepItemDescriptor<Id extends string>(\n step: StepItemDescriptor<Id>,\n): void {\n assertNonEmpty(step.id, \"item id\");\n if (step.id !== step.id.trim()) {\n throw new TypeError(\"Steps item id must not start or end with whitespace\");\n }\n assertNonEmpty(step.label, \"item label\");\n if (step.description !== undefined) {\n assertNonEmpty(step.description, \"item description\");\n }\n}\n\nexport function validateStepsDescriptor<Id extends string>(\n descriptor: StepsDescriptor<Id>,\n): void {\n if (descriptor.navigationMode !== undefined && descriptor.navigationMode !== \"linear\" && descriptor.navigationMode !== \"free\") {\n throw new TypeError(`Unsupported Steps navigationMode: ${String(descriptor.navigationMode)}`);\n }\n if (descriptor.steps.length < 2) {\n throw new RangeError(\"Steps must contain at least two steps\");\n }\n const ids = new Set<Id>();\n for (const step of descriptor.steps) {\n validateStepItemDescriptor(step);\n if (ids.has(step.id)) {\n throw new TypeError(`Duplicate Steps item id: ${step.id}`);\n }\n ids.add(step.id);\n }\n if (!ids.has(descriptor.currentStepId)) {\n throw new RangeError(\n `Steps currentStepId does not match any step id: ${String(descriptor.currentStepId)}`,\n );\n }\n if (\n descriptor.currentStepStatus !== undefined &&\n descriptor.currentStepStatus !== \"current\" &&\n descriptor.currentStepStatus !== \"error\" &&\n descriptor.currentStepStatus !== \"complete\"\n ) {\n throw new TypeError(\n `Unsupported Steps currentStepStatus: ${String(descriptor.currentStepStatus)}`,\n );\n }\n}\n\nexport function validateStepsStatusLabels(labels: StepsStatusLabels): void {\n if (labels === null || typeof labels !== \"object\") {\n throw new TypeError(\"Steps statusLabels must be an object\");\n }\n for (const key of [\"pending\", \"current\", \"complete\", \"error\"] as const) {\n const value = labels[key];\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new TypeError(`Steps statusLabels.${key} must not be empty`);\n }\n }\n}\n\n/** A step is visually and semantically \"reached\" once it is no longer waiting. */\nexport function isStepReached(status: StepStatus): boolean {\n return status !== \"pending\";\n}\n\n/**\n * Derives every step's status from its position relative to the single\n * cursor, then attaches the order-preserving accessible name and the\n * supplementary status announcement. Throws on a malformed descriptor,\n * labels bag, or composer instead of silently rendering an inconsistent flow.\n */\nexport function resolveStepsDescriptor<Id extends string>(\n descriptor: StepsDescriptor<Id>,\n options: ResolveStepsOptions,\n): readonly ResolvedStepDescriptor<Id>[] {\n validateStepsDescriptor(descriptor);\n validateStepsStatusLabels(options.statusLabels);\n if (typeof options.composeAccessibleName !== \"function\") {\n throw new TypeError(\"Steps composeAccessibleName must be a function\");\n }\n\n const currentStepStatus =\n descriptor.currentStepStatus ?? stepsDefaults.currentStepStatus;\n const total = descriptor.steps.length;\n const cursorIndex = descriptor.steps.findIndex(\n (step) => step.id === descriptor.currentStepId,\n );\n\n return descriptor.steps.map((step, index) => {\n const status: StepStatus =\n index < cursorIndex\n ? \"complete\"\n : index > cursorIndex\n ? \"pending\"\n : currentStepStatus;\n const position = index + 1;\n const accessibleName = options.composeAccessibleName({\n position,\n total,\n label: step.label,\n });\n if (typeof accessibleName !== \"string\" || accessibleName.trim().length === 0) {\n throw new TypeError(\n \"Steps composeAccessibleName must return a non-empty string\",\n );\n }\n return {\n ...step,\n status,\n position,\n total,\n accessibleName,\n statusLabel: options.statusLabels[status],\n revisitable: index < cursorIndex,\n };\n });\n}\n\nexport const stepsRecipe = {\n slots: [\n \"root\",\n \"step\",\n \"indicator\",\n \"marker\",\n \"connector\",\n \"label\",\n \"description\",\n ] as const,\n gap: spacing.xs,\n // Spint review (2026-10-21): keep endpoint marks inset and give illustrated marks breathing room.\n paddingInline: spacing.md,\n indicator: {\n size: glyph.xxl,\n borderWidth: stroke.default,\n activeBorderWidth: stroke.strong,\n /** null means the renderer shows the step's own position number instead of a glyph. */\n marks: {\n pending: null,\n current: null,\n complete: \"check\",\n error: \"error\",\n },\n border: {\n pending: semanticColors.border.default,\n current: semanticColors.content.brand,\n complete: semanticColors.feedback.success.border,\n error: semanticColors.feedback.danger.border,\n },\n background: {\n pending: null,\n current: null,\n complete: semanticColors.feedback.success.badgeBackground,\n error: semanticColors.feedback.danger.badgeBackground,\n },\n content: {\n pending: semanticColors.content.secondary,\n current: semanticColors.content.brand,\n complete: semanticColors.feedback.success.foreground,\n error: semanticColors.feedback.danger.foreground,\n },\n },\n connector: {\n height: stroke.default,\n tone: {\n reached: semanticColors.content.brand,\n unreached: semanticColors.border.default,\n },\n },\n label: {\n textVariant: \"label\",\n fontWeight: fontWeight.medium,\n color: {\n pending: semanticColors.content.secondary,\n current: semanticColors.content.primary,\n complete: semanticColors.content.primary,\n error: semanticColors.feedback.danger.foreground,\n },\n },\n description: {\n textVariant: \"caption\",\n color: semanticColors.content.secondary,\n },\n /** A revisitable step is one button over its marker and label; the shared touch minimum applies to the whole cell. */\n press: {\n minTouchTarget: control.minTouchTarget,\n },\n} as const satisfies {\n slots: readonly string[];\n gap: number;\n paddingInline: number;\n indicator: {\n size: number;\n borderWidth: number;\n activeBorderWidth: number;\n marks: Record<StepStatus, SemanticIconName | null>;\n border: Record<StepStatus, ColorReference>;\n background: Record<StepStatus, ColorReference | null>;\n content: Record<StepStatus, ColorReference>;\n };\n connector: {\n height: number;\n tone: Record<\"reached\" | \"unreached\", ColorReference>;\n };\n label: {\n textVariant: TextVariant;\n fontWeight: FontWeightValue;\n color: Record<StepStatus, ColorReference>;\n };\n description: {\n textVariant: TextVariant;\n color: ColorReference;\n };\n press: {\n minTouchTarget: number;\n };\n};\n"]}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAiB,CAAC","sourcesContent":["/** Package release shown by documentation surfaces. Kept in sync by a test. */\nexport const designSystemVersion = \"1.38.0\" as const;\n"]}
|
package/docs/collection-rail.md
CHANGED
|
@@ -28,6 +28,9 @@ Native row만 RTL에서 역방향으로 배치하며 데이터/React key/읽기
|
|
|
28
28
|
삭제된 anchor는 첫 항목으로 대체한다. key/remount로 입력을 초기화하지 않는다.
|
|
29
29
|
- 세부 모달은 `Card.actions`→기존 Dialog를 합성한다. rail은 body overflow·background lock·focus trap을 따로 구현하지 않는다.
|
|
30
30
|
|
|
31
|
+
2026-10-20 스핀트 사용자 요청: 스와이프와 중복되는 버튼을 제거할 수 있도록 두 renderer에
|
|
32
|
+
`showNavigation`(기본 `true`)을 제공한다. `false`는 버튼 행과 여백만 제거하며 host 스크롤·초점 노출·Web 키보드 탐색은 유지한다.
|
|
33
|
+
|
|
31
34
|
## 배치와 플랫폼 경계
|
|
32
35
|
|
|
33
36
|
comfortable 최대 폭360은 `layout.readingMaxWidth / 2`, compact240은 `/3`이다. 실제 폭은 viewport와
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Component maturity and evidence manifest
|
|
2
2
|
|
|
3
|
-
> Generated from `@hjmds/design-contracts` 1.
|
|
3
|
+
> Generated from `@hjmds/design-contracts` 1.38.0. Do not edit this file directly; run `pnpm contracts:sync`.
|
|
4
4
|
|
|
5
5
|
This document is the human-readable projection of the same catalog and Showcase manifest consumed by product CI.
|
|
6
6
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"designSystemVersion": "1.
|
|
3
|
+
"designSystemVersion": "1.38.0",
|
|
4
4
|
"source": "packages/design-contracts/docs/generated/showcase-manifest.json",
|
|
5
5
|
"surfaces": {
|
|
6
6
|
"web": {
|
|
7
7
|
"packageName": "@hjmds/react",
|
|
8
|
-
"packageVersion": "1.
|
|
8
|
+
"packageVersion": "1.38.0",
|
|
9
9
|
"activeComponents": 131,
|
|
10
10
|
"claimedComponents": 131,
|
|
11
11
|
"completeScenarioComponents": 130,
|
|
@@ -7062,7 +7062,7 @@
|
|
|
7062
7062
|
},
|
|
7063
7063
|
"native": {
|
|
7064
7064
|
"packageName": "@hjmds/react-native",
|
|
7065
|
-
"packageVersion": "1.
|
|
7065
|
+
"packageVersion": "1.38.0",
|
|
7066
7066
|
"activeComponents": 113,
|
|
7067
7067
|
"claimedComponents": 113,
|
|
7068
7068
|
"completeScenarioComponents": 110,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Renderer evidence coverage
|
|
2
2
|
|
|
3
|
-
> Generated for HJM 1.
|
|
3
|
+
> Generated for HJM 1.38.0. Do not edit directly; run `pnpm evidence:sync`.
|
|
4
4
|
|
|
5
5
|
This projection joins the canonical surface maturity manifest with first-party renderer claims. Missing scenarios are explicit beta promotion debt; stable surfaces are blocked by CI until none remain.
|
|
6
6
|
|
package/docs/theme-palette.md
CHANGED
|
@@ -95,3 +95,10 @@ Ant의 원시 색상이나 낮은 테두리 대비까지 복제하지 않고, HJ
|
|
|
95
95
|
사용자가 제시한 오늘야구 경기 상태 배지의 단색 판과 흰 글자를 양 테마에서 유지하도록 `neutralFill`(#4e5968)·`strongFill`(#333d4b)을 추가했다. 기존 textMuted/text는 다크 모드에서 밝아지므로 판에 재사용하지 않는다. 상태색은 기존 `accentFill`과 `dangerFill`, 브랜드는 primary를 사용한다. 일반 본문과 Tag의 색 계약은 유지한다.
|
|
96
96
|
|
|
97
97
|
선택의창의 밝은 다크 브랜드 primary는 어두운 `onPrimary`를 사용하므로 이를 상태 배지의 회색·성공색 위에 재사용하면 대비가 무너진다. `onStatusFill`을 별도 역할로 두고 상태 채움의 글자와 아이콘에 함께 적용한다. 브랜드와 위험 채움은 각각 기존 `onPrimary`·`onDanger` 짝을 유지한다.
|
|
98
|
+
|
|
99
|
+
## 밝은 표면의 기본값 변경 (2026-10-21)
|
|
100
|
+
|
|
101
|
+
오늘야구의 기본 테마 적용 뒤 표와 카드의 회색 면이 두드러진다는 사용자 피드백에 따라,
|
|
102
|
+
이전 제품 표면색을 HJM의 밝은 테마 기본값으로 채택한다. `surface`는 `#ffffff`,
|
|
103
|
+
`surfaceAlt`는 `#ebeef2`다. `bg`는 기존 흰색을 유지하며 `raised` 카드도 기존처럼 `bg`를 쓴다.
|
|
104
|
+
다크 테마와 브랜드·상태 강조색은 이번 결정의 변경 대상이 아니다. 웹·Native가 같은 `THEMES`를 소비한다.
|
package/docs/usage/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
버튼 위치에서 시작해 구성·컴포넌트 지침으로 내려가고, 값은 토큰 지침에서 고른다.
|
|
7
7
|
[선택 기준](SELECTION.md)으로 그대로 사용·공개 슬롯 확장·공통 API 보완·제품 고유 기능 연결을 고른다. 설치 버전의 지침을 본다: `node_modules/@hjmds/design-contracts/docs/usage/`.
|
|
8
8
|
제공 기준은 확인한 기본 API 버전이며 최초 출시 버전이 아니다. 구성·화면은 각 API 지침의 제공 버전을 확인한다.
|
|
9
|
-
API 안정성은 1.
|
|
9
|
+
API 안정성은 1.37.2의 생성 manifest에서 읽은 정규(canonical) API 기준이다. 동반·선택형 확장은 개별 계약을 확인한다.
|
|
10
10
|
Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품 반영을 의미하지 않는다.
|
|
11
11
|
`분류`는 Storybook 제목 `<배포|실험>/<단계>/<분류>/<항목>`의 셋째 마디이고, 토큰·구성·화면은 Storybook 메뉴와 같은 순서다.
|
|
12
12
|
|
|
@@ -43,7 +43,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
43
43
|
| [AspectRatio](components/aspect-ratio.md) | 레이아웃 | 이미지·동영상·지도처럼 늦게 로드되는 매체의 자리를 미리 잡아 레이아웃 흔들림을 막을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
44
44
|
| [Asset](components/asset.md) | 데이터 표시 | 아이콘·이미지·Lottie·비디오를 같은 크기·모서리 규칙의 액자에 넣을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
45
45
|
| [AuthProviderButton](components/auth-provider-button.md) | 동작 | Google·Kakao·Naver·Apple 소셜 로그인 버튼에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
46
|
-
| [AuthScreenLayout](components/auth-screen-layout.md) | 레이아웃 | 로그인·가입 진입 화면의 배치에 쓴다. | 1.12.1
|
|
46
|
+
| [AuthScreenLayout](components/auth-screen-layout.md) | 레이아웃 | 로그인·가입 진입 화면의 배치에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
47
47
|
| [Avatar](components/avatar.md) | 데이터 표시 | 사람·계정을 사진 또는 이니셜로 나타낼 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
48
48
|
| [Badge](components/badge.md) | 데이터 표시 | 항목의 상태나 분류를 짧은 글자 하나로 붙일 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
49
49
|
| [BezierEditor](components/bezier-editor.md) | 구성/직접 조작과 모션 | CSS `cubic-bezier` 이징 네 좌표를 슬라이더로 편집하고 곡선과 움직임 미리 보기를 함께 보여 줄 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
@@ -58,8 +58,8 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
58
58
|
| [Celebration](components/celebration.md) | 구성/직접 조작과 모션 | 목표 달성, 첫 완료처럼 드물게 일어나는 성공 순간에 한 번 터지는 색종이 효과에 쓴다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
59
59
|
| [Chart](components/chart.md) | 데이터 표시 | 수치의 흐름·비교·몫·모양·관계를 그림으로 읽게 할 때 쓴다. | 1.21.0 | Web · Native | Web beta / Native beta | 배포 |
|
|
60
60
|
| [ChatDateSeparator](components/chat-date-separator.md) | 데이터 표시 | 대화 타임라인에서 날짜(또는 오래 쉰 뒤의 시각)가 바뀌는 자리에 가운데 정렬 구분 줄을 둘 때 쓴다. | 1.31.0 | Web · Native | Web beta / Native beta | 배포 |
|
|
61
|
-
| [ChatMessage](components/chat-message.md) |
|
|
62
|
-
| [ChatScreen](components/chat-screen.md) |
|
|
61
|
+
| [ChatMessage](components/chat-message.md) | 데이터 표시 | DM·대화 타임라인의 메시지 한 개에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
62
|
+
| [ChatScreen](components/chat-screen.md) | 레이아웃 | DM·대화방처럼 헤더, 메시지 타임라인, 하단 작성창으로 이루어진 화면 한 장에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
63
63
|
| [Checkbox](components/checkbox.md) | 입력 | 독립된 예/아니오 하나를 고르는 항목에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
64
64
|
| [CheckboxGroup](components/checkbox-group.md) | 입력 | 한 질문에 대한 여러 선택지 중 0개 이상을 고르게 할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
65
65
|
| [Chip](components/chip.md) | 입력 | 누를 수 있는 작은 pill이다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
@@ -69,9 +69,9 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
69
69
|
| [ColorPicker](components/color-picker.md) | 입력 | 사용자가 콘텐츠 색(라벨 색, 태그 색, 테마 편집기의 사용자 값 등)을 sRGB HEX로 고르는 폼 입력에 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
|
|
70
70
|
| [Combobox](components/combobox.md) | 입력 | 주어진 목록에서 하나를 고르는데 목록이 길어 입력으로 좁혀야 할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
71
71
|
| [CommandPalette](components/command-palette.md) | 오버레이 | ⌘K 스타일로 앱 전체의 **행동**을 검색해 실행하는 모달에 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
|
|
72
|
-
| [CommentThreadRow](components/comment-thread-row.md) |
|
|
73
|
-
| [CommentThreadScreen](components/comment-thread-screen.md) |
|
|
74
|
-
| [CompositionLayout](components/composition-layout.md) |
|
|
72
|
+
| [CommentThreadRow](components/comment-thread-row.md) | 데이터 표시 | 가상화 목록(FlatList·가상 스크롤러)처럼 제품이 댓글 목록을 직접 그려야 할 때 댓글 한 행을 그린다. | 1.23.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
73
|
+
| [CommentThreadScreen](components/comment-thread-screen.md) | 레이아웃 | 게시물·콘텐츠 아래의 댓글 화면 한 장에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
74
|
+
| [CompositionLayout](components/composition-layout.md) | 레이아웃 | 카드·패널·화면 본문 안의 콘텐츠 묶음에 제목·배지·행동·안내·본문·하단 영역이 필요할 때 쓴다. | 1.23.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
75
75
|
| [Container](components/container.md) | 레이아웃 | 화면 본문의 최대 폭과 좌우(논리 방향) 여백을 맞출 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
76
76
|
| [ContentTransition](components/content-transition.md) | 시각 효과 | 같은 자리의 내용이 상태에 따라 바뀔 때(필터 결과 패널, 단계별 본문) 새 내용이 짧게 나타나도록 감싼다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
77
77
|
| [ContextMenu](components/context-menu.md) | 탐색 | Web에서 제품이 소유한 영역(카드·목록 행·캔버스)의 우클릭·길게 누르기·Shift+F10에 명령 목록을 띄울 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native unsupported | 배포 |
|
|
@@ -86,7 +86,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
86
86
|
| [DesignSystemProvider](components/design-system-provider.md) | 기반 기능 | 앱 루트에 한 번 둔다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
87
87
|
| [Dialog](components/dialog.md) | 오버레이 | 화면 흐름을 잠시 멈추고 사용자의 주의가 필요한 **짧은 작업**에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
88
88
|
| [Divider](components/divider.md) | 레이아웃 | 서로 다른 내용 묶음 사이에 얇은 구분선이 필요할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
89
|
-
| [EditorScreen](components/editor-screen.md) |
|
|
89
|
+
| [EditorScreen](components/editor-screen.md) | 레이아웃 | 글쓰기·프로필 수정처럼 한 화면 전체가 편집 흐름일 때 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
90
90
|
| [EffectSurface](components/effect-surface.md) | 시각 효과 | 환영·온보딩·빈 히어로처럼 분위기를 주는 배경이 필요할 때 내용 뒤에 장식 레이어(mesh·glow·grain)를 깐다. | 1.12.1 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
91
91
|
| [EmptyState](components/empty-state.md) | 상태와 알림 | 목록이 비었거나 검색 결과가 0건이라 **아직 없음**을 알릴 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
92
92
|
| [EventAttendanceCard](components/event-attendance-card.md) | 구성/선택과 필터 | 행사·모임 카드에서 일정, 참석자 수, 참석자 목록을 보여 주고 참석 응답(참석·미정·불참 등)과 캘린더 추가를 실제 요청으로 처리할 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
@@ -104,7 +104,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
104
104
|
| [IconButton](components/icon-button.md) | 동작 | 보이는 글자 없이 아이콘만으로 표시하는 행동에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
105
105
|
| [IconTile](components/icon-tile.md) | 데이터 표시 | 프로필·런처 격자의 아이콘 위/제목 아래 항목에 쓴다. | 1.26.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
106
106
|
| [Image](components/image.md) | 데이터 표시 | 원본 크기를 아는 사진·차트 이미지를 로드 전에 자리를 잡아 두고, 실패해도 의미를 잃지 않게 보여 줄 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
107
|
-
| [ImageChoiceGroup](components/image-choice-group.md) |
|
|
107
|
+
| [ImageChoiceGroup](components/image-choice-group.md) | 입력 | 아바타 얼굴, 프로필 사진, 모음에 넣을 사진처럼 **그림을 보고 고르는** 단일·다중 선택에 쓴다. | 미게시(1.26.1 이후) | Web · Native | Web beta / Native beta | 배포 |
|
|
108
108
|
| [ImageComparison](components/image-comparison.md) | 데이터 표시 | 같은 좌표와 비율의 두 이미지를 겹쳐 변화량을 비교할 때 쓴다. | 1.14.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
109
109
|
| [ImportFieldMapping](components/import-field-mapping.md) | 구성/입력과 작성 | CSV 등 원본 열을 제품의 대상 필드에 연결하고, 검증을 통과한 연결만 가져오기로 넘길 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
110
110
|
| [InlineEdit](components/inline-edit.md) | 구성/입력과 작성 | 목록·상세의 제목처럼 이미 확정된 짧은 값 하나를 그 자리에서 명시적으로 편집·저장·취소할 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
@@ -116,34 +116,34 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
116
116
|
| [Layout](components/layout.md) | 레이아웃 | 앱의 상시 골격(header · sidebar · main · footer)을 한 번 세울 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
117
117
|
| [Link](components/link.md) | 동작 | 사용자가 복사하거나 새 탭으로 열 수 있는 **목적지**로 이동할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
118
118
|
| [List](components/list.md) | 데이터 표시 | 이미 다 불러온, 개수가 많지 않은 행들을 이름 있는 목록 하나로 묶을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
119
|
-
| [ListDetailScreen](components/list-detail-screen.md) |
|
|
119
|
+
| [ListDetailScreen](components/list-detail-screen.md) | 레이아웃 | 목록 화면에서 한 항목의 상세를 같은 화면 안에서 열고, 뒤로 오면 목록의 입력·스크롤이 그대로 남아야 할 때 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
120
120
|
| [ListRow](components/list-row.md) | 데이터 표시 | 목록의 한 줄에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
121
121
|
| [LoadMore](components/load-more.md) | 탐색 | 이미 그린 항목을 그대로 둔 채 목록 끝에서 다음 페이지를 요청하는 footer에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
122
122
|
| [Masonry](components/masonry.md) | 레이아웃 | 높이가 서로 다른 카드(사진 피드, 핀보드, 갤러리)를 여러 열에 빈틈없이 쌓을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
123
|
-
| [MediaSelectionScreen](components/media-selection-screen.md) |
|
|
123
|
+
| [MediaSelectionScreen](components/media-selection-screen.md) | 레이아웃 | 고른 사진·영상을 큰 썸네일 격자로 보여 주고, 각 항목의 업로드 상태·재시도·취소·순서 이동·삭제와 "추가"·"완료" 행동을 한 화면에 묶을 때 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
124
124
|
| [MediaTransport](components/media-transport.md) | 구성/직접 조작과 모션 | 제품의 실제 오디오·영상 엔진에 재생·일시 정지·유한 길이 탐색 제어를 붙일 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
125
125
|
| [Mentions](components/mentions.md) | 입력 | 여러 줄 입력 중 `@`(사람)·`#`(해시태그) 같은 트리거를 치면 후보를 띄우고, 고른 후보를 트리거부터 커서까지 자리에 넣고 공백 하나를 붙이는 입력에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
126
126
|
| [Menu](components/menu.md) | 탐색 | 트리거 버튼을 누르면 뜨는 **항목 목록**에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
127
127
|
| [Menubar](components/menubar.md) | 탐색 | 데스크톱 Web 앱 상단에 항상 같은 자리에 있는 가로 메뉴 막대(파일·편집·보기)에 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
|
|
128
|
-
| [MessageComposer](components/message-composer.md) |
|
|
129
|
-
| [ModerationScreen](components/moderation-screen.md) |
|
|
128
|
+
| [MessageComposer](components/message-composer.md) | 입력 | 채팅·DM·댓글 입력창에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
129
|
+
| [ModerationScreen](components/moderation-screen.md) | 레이아웃 | 게시물·댓글·사용자 **신고** 화면에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
130
130
|
| [Notice](components/notice.md) | 상태와 알림 | 저장된 항목·폼 안의 작은 성공 상태는 `appearance="status"`(1.37.0)를 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
131
|
-
| [NotificationInboxScreen](components/notification-inbox-screen.md) |
|
|
132
|
-
| [NotificationItem](components/notification-item.md) |
|
|
131
|
+
| [NotificationInboxScreen](components/notification-inbox-screen.md) | 레이아웃 | 알림함 화면 전체 틀에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
132
|
+
| [NotificationItem](components/notification-item.md) | 데이터 표시 | 알림함의 알림 한 행에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
133
133
|
| [NotificationRow](components/notification-row.md) | 데이터 표시 | 알림 목록에서 아바타, 유형 배지, 미리보기, 시간, 읽지 않음 표시, 선택적 썸네일을 나란히 배치한다. | 1.26.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
134
134
|
| [NumberField](components/number-field.md) | 입력 | 범위가 정해진 **정확한 수 하나**를 입력받을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
135
|
-
| [OnboardingScreen](components/onboarding-screen.md) |
|
|
135
|
+
| [OnboardingScreen](components/onboarding-screen.md) | 레이아웃 | 첫 실행 소개·초기 설정처럼 **몇 단계를 차례로 넘기는 화면**에 쓴다. | 1.14.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
136
136
|
| [OtpField](components/otp-field.md) | 입력 | 문자·메일로 받은 **숫자 인증번호**를 칸 모양으로 입력받을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
137
137
|
| [OverviewScreen](components/overview-screen.md) | 레이아웃 | 같은 데이터와 기능을 유지하면서 테마별 행·카드·격자와 도구 배치를 선택하는 목록 화면에 쓴다. | 1.15.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
138
138
|
| [PageIndicator](components/page-indicator.md) | 탐색 | 제품이 직접 만든 가로 페이저(Native 페이징 `FlatList`, Web scroll-snap 줄) 아래에 지금 몇 번째 페이지인지 점으로 보여 주고, 점을 눌러 그 페이지로 옮길 때 쓴다. | 1.31.0 | Web · Native | Web beta / Native beta | 배포 |
|
|
139
139
|
| [Pagination](components/pagination.md) | 탐색 | 총 개수(또는 총 페이지 수)가 정해진 결과 집합에서 사용자가 **임의의 페이지로 바로 이동**해야 할 때 Web·Native에서 쓴다. | 1.12.1 | Web · Native | Web stable / Native beta | 배포 |
|
|
140
140
|
| [PasswordField](components/password-field.md) | 입력 | 비밀번호를 입력받고, 필요할 때만 값을 눈으로 확인하게 할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
141
141
|
| [PasswordRulesField](components/password-rules-field.md) | 구성/입력과 작성 | 새 비밀번호 입력 아래에 제품이 정한 규칙의 충족 개수와 규칙별 상태를 함께 보여 줄 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
142
|
-
| [PermissionScreen](components/permission-screen.md) |
|
|
143
|
-
| [PhotoSourceSheet](components/photo-source-sheet.md) |
|
|
142
|
+
| [PermissionScreen](components/permission-screen.md) | 레이아웃 | 카메라·위치·알림 같은 권한이 **왜 필요한지 설명하고 다음 행동을 고르게 하는** 화면에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
143
|
+
| [PhotoSourceSheet](components/photo-source-sheet.md) | 오버레이 | 사진 버튼 하나에서 **앨범에서 고르기 / 촬영하기**를 고르게 할 때 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
144
144
|
| [Popover](components/popover.md) | 오버레이 | 트리거에 붙어 뜨는 비모달 표면 안에 **포커스를 받는 임의 콘텐츠**를 둘 때 쓴다. | 1.12.1 | Web | Web stable / Native unsupported | 배포 |
|
|
145
145
|
| [PricingChoice](components/pricing-choice.md) | 구성/선택과 필터 | 요금제·결제 주기 중 하나를 고르고, 선택한 실제 가격과 혜택을 확인한 뒤 [계속]으로 제품 결제 흐름을 시작할 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
146
|
-
| [ProfileScreen](components/profile-screen.md) |
|
|
146
|
+
| [ProfileScreen](components/profile-screen.md) | 레이아웃 | 내 프로필(또는 계정) 화면 틀에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
147
147
|
| [ProfileSummary](components/profile-summary.md) | 데이터 표시 | 프로필의 아바타·이름·배지·통계 수치·행동을 구성한다. | 1.26.0 | Web · Native | Web stable / Native stable | 배포 |
|
|
148
148
|
| [Progress](components/progress.md) | 상태와 알림 | 작업이 얼마나 진행됐는지 보여 줄 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
149
149
|
| [ProgressiveBlur](components/progressive-blur.md) | 시각 효과 | 스크롤 영역의 바깥에 더 내용이 있음을 알리거나 장식 이미지 가장자리를 흐릴 때 쓴다. | 1.14.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
@@ -156,14 +156,14 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
156
156
|
| [ReplyMessage](components/reply-message.md) | 구성/입력과 작성 | 대화 타임라인의 메시지에 실제 메시지 id를 묶은 답장 동작을 붙일 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
157
157
|
| [Result](components/result.md) | 상태와 알림 | 사용자 행동 뒤 흐름이 **끝난** 화면에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
158
158
|
| [SavedItemsScreen](components/saved-items-screen.md) | 화면/콘텐츠 | 저장한 이미지·게시물을 컬렉션 표지 → 사진 격자 → 상세 순서로 탐색할 때 쓴다. | 1.17.1 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
159
|
-
| [ScreenLayout](components/screen-layout.md) |
|
|
159
|
+
| [ScreenLayout](components/screen-layout.md) | 레이아웃 | 한 라우트 화면의 뼈대가 필요할 때 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
160
160
|
| [SearchField](components/search-field.md) | 입력 | 목록·화면 안에서 검색어를 입력받을 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
161
161
|
| [SearchScreen](components/search-screen.md) | 레이아웃 | 검색어 입력, 필터, 최근 검색, 결과 목록을 갖춘 검색 화면 전체에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
162
162
|
| [Section](components/section.md) | 레이아웃 | 화면 안의 내용 묶음에 제목·설명·머리 행동(“모두 보기”, “편집”)을 붙일 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
163
163
|
| [SegmentedControl](components/segmented-control.md) | 입력 | 2~4개의 짧은 보기 중 **항상 하나가 선택된** 전환에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
164
164
|
| [Select](components/select.md) | 입력 | 폼 한 칸에서 여러 선택지 중 하나를 고르게 할 때 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
165
165
|
| [SelectionActionBar](components/selection-action-bar.md) | 구성/선택과 필터 | 목록에서 여러 항목을 고른 뒤 보관·삭제 같은 같은 행동을 한 번에 실행하고, 방금 확정된 작업만 되돌릴 때 쓴다. | 1.21.0 | Web · Native | 개별 API 계약 참조 | 배포 |
|
|
166
|
-
| [SettingsScreen](components/settings-screen.md) |
|
|
166
|
+
| [SettingsScreen](components/settings-screen.md) | 레이아웃 | 앱의 설정 화면 전체에 쓴다. | 1.17.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
167
167
|
| [SharedTransitionElement](components/shared-transition-element.md) | 구성/직접 조작과 모션 | 목록의 카드(사진·썸네일)를 눌러 상세 화면으로 갈 때, 같은 요소가 두 화면 사이에서 확대·축소되어 이어지는 공유 요소 전환에 쓴다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
|
|
168
168
|
| [SharedTransitionScreen](components/shared-transition-screen.md) | 구성/직접 조작과 모션 | `createHjmTransitionStack()`으로 만든 stack에서 공유 요소 전환을 쓸 때, **각 라우트 본문**을 감싼다. | 1.12.1 | Native | 개별 API 계약 참조 | 배포 |
|
|
169
169
|
| [Sheet](components/sheet.md) | 오버레이 | 현재 화면 위에 모달로 띄우는 보조 작업 패널에 쓴다. | 1.12.1 | Web · Native | Web stable / Native stable | 배포 |
|
|
@@ -262,7 +262,7 @@ Storybook 배포/실험은 예제 분류이며 npm 게시·기기 검증·제품
|
|
|
262
262
|
| [보관과 실행 취소](compositions/action-recovery-undo.md) | 피드백과 복구 | 보관·숨기기·목록에서 빼기처럼 제품이 역연산을 제공하는 작업 뒤에, 같은 자리에서 실행 취소를 주고 그 복구 요청이 성공해야 화면을 되돌릴 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
|
|
263
263
|
| [선택과 오류 복구](compositions/upload-recovery.md) | 피드백과 복구 | 파일 선택과 전송 상태의 취소·재시도를 연결할 때 쓴다. | 1.14.0 | Web · Native | 구성 API별 | 배포 |
|
|
264
264
|
| [스트림 응답과 중지](compositions/streaming-answer.md) | 피드백과 복구 | 실제 provider가 보내는 응답 delta를 표시하고 중지·재생성·오류를 같은 영역에서 제공할 때 쓴다. | 1.21.0 | Web · Native | 구성 API별 | 배포 |
|
|
265
|
-
| [저장 결과](compositions/save-feedback.md) | 피드백과 복구 |
|
|
265
|
+
| [저장 결과](compositions/save-feedback.md) | 피드백과 복구 | 실제 영속 저장 성공 뒤 짧게 결과를 알린다. | 1.37.1 | Web · Native | 구성 API별 | 실험 |
|
|
266
266
|
| [저장과 재시도](compositions/action-recovery-save.md) | 피드백과 복구 | 입력한 내용을 서버에 저장하는 폼 한 덩어리에서 저장 중 중복 실행을 막고, 실패하면 입력을 지우지 않은 채 제출했던 값 그대로 다시 보낼 때 쓴다. | 1.12.1 | Web · Native | 구성 API별 | 배포 |
|
|
267
267
|
| [전송 진행과 취소](compositions/transfer-task.md) | 피드백과 복구 | 실제 다운로드·업로드·작업의 시작/대기/진행·취소·실패 복구를 한 구성으로 제공할 때 쓴다. | 1.21.0 | Web · Native | 구성 API별 | 배포 |
|
|
268
268
|
| [중단해도 남는 현재 상태](compositions/expo-interactions.md) | 피드백과 복구 | 버튼으로 상태를 빠르게 바꾸거나 전환 도중 내용을 닫아도 현재 상태가 바로 보이고 남아야 하는 영역에 쓴다. | 1.12.1 | Native | 구성 API별 | 배포 |
|
package/docs/usage/SELECTION.md
CHANGED
|
@@ -79,11 +79,11 @@ HJM의 기존 API를 확장하거나 하위 구성을 공개한다. 도메인
|
|
|
79
79
|
| 상태·결과 안내 | `Notice`, `Badge`, `ListRow` 설명 | 가운데 정렬 일반 글자 |
|
|
80
80
|
| 예제 전용 도구(실패 예약 등) | "미리보기 도구" 캡션 아래 `Button size="small" tone="ghost"`로 분리 | 제품 행동과 같은 모양·폭 |
|
|
81
81
|
| 헤더 제목·추가 기능 | 하단 탭·상단 탐색과 중복되는 최상위 화면명은 `titleVisibility="hidden"`. 식별에 필요한 대상·하위 작업명은 유지하며 본문과 중복되면 생략. 보이는 제목은 기본 가운데(`ScreenLayout`·`TopBar`, 왼쪽 정렬은 선택), 도구는 `actions` 아이콘 1~2개(넘치면 Menu) | 모든 제목 일괄 숨김, 위치 이름의 반복, 헤더의 글자 도구 버튼 |
|
|
82
|
-
| 상단 탐색과 도구 한 행 | `ScreenLayout.header`에 HJM `TopBar`, `leading`에 HJM `Tabs`/`SegmentedControl` 탐색, `actions`에 아이콘 도구. 탐색이 화면명을 대신한다. 본문 추가 필터는 `notice`/본문 유지 | 제목 행 + 같은 화면을 설명하는 탐색 행, 앱에서 직접 만든 헤더 레이아웃 |
|
|
82
|
+
| 상단 탐색과 도구 한 행 | `ScreenLayout.header`에 HJM `TopBar`, `leading`에 HJM `Tabs`/`SegmentedControl` 탐색, `actions`에 아이콘 도구. 탐색이 화면명을 대신한다. 본문 추가 필터는 `notice`/본문 유지 상단 일반 도구는 `size="small" hitSlop={0}`·간격 `spacing.xxs` 4를 사용한다([TopBar](components/top-bar.md#상단-도구의-공통-크기), 2026-10-21 사용자 승인). | 제목 행 + 같은 화면을 설명하는 탐색 행, 앱에서 직접 만든 헤더 레이아웃 |
|
|
83
83
|
| 상단 검색·장소 입력 | `SearchScreen`의 `queryLabelVisibility="hidden"`(기본). `queryLabel`은 placeholder와 접근성 이름, 뒤로가기·입력·오른쪽 도구는 공통 행에서 세로 중앙 | 입력 위 안내 label로 행 높이를 늘리기, 아이콘을 label 포함 높이에 맞추기, 일반 폼 label까지 숨기기 |
|
|
84
84
|
| 도구성 행동(새로고침·추가·편집·삭제·공유·검색·필터·더보기) | `IconButton` + 아이콘, 글자는 `label`(보조기술 이름). HJM 화면은 아이콘 슬롯(`ScreenFlowAction.icon` 등) | 헤더·행·도구 막대의 글자 버튼 |
|
|
85
85
|
| 화면 나가기(뒤로·닫기·설정 나가기) | `ScreenLayout`(과 이를 쓰는 화면)·`AuthScreenLayout`의 `exit` — 헤더 시작 쪽 아이콘 버튼, 글자 없음. 단계형 작성의 앞 단계는 `EditorScreen` `stepBack`(1.31.0) | 본문·하단의 "나가기" 글자 버튼, 화면마다 다른 화살표, 로그인·작성 화면 위에 따로 얹은 `TopBar` |
|
|
86
|
-
| 버튼 묶음 간격 | 가로·세로
|
|
86
|
+
| 버튼 묶음 간격 | 일반 행동 묶음은 가로·세로 `gap="xs"`(8) — `actionGroupRecipe`. 상단 도구 줄만 `gap="xxs"`(4), `size="small" hitSlop={0}`(2026-10-21 사용자 승인) | 세로 버튼 사이 12 이상 |
|
|
87
87
|
| 나란한 컴포넌트 높이 | 항상 같은 높이로 위·아래 경계를 맞춘다. 버튼·필터·탭·선택 카드·입력/아이콘 도구 모두 포함. 여러 줄이면 같은 행의 가장 높은 콘텐츠에 맞추며 줄바꿈 그리드는 행별 적용. 세로의 동일 행동 묶음도 같은 크기 규격. 공통 renderer/그리드가 소유(2026-10-20 다에리 선택 카드 높이 불일치 사용자 지적) | 문구·아이콘·선택/강조 상태마다 다른 높이, 내용 자르기·글자 축소·넘치는 고정 높이로 맞추기 |
|
|
88
88
|
| 상단 영역과 본문 연결 | 상태바·노치/Dynamic Island safe area → 헤더 → 본문 첫 배경을 연속된 바탕으로 연결한다. 안내·캐릭터/말풍선·브랜드 영역의 배경/표면 전환·좌우 경계·여백도 연결한다. 고정/접힘 상단은 실제 높이를 공통 화면/스크롤 host에 반영해 본문을 가리지 않는다(2026-10-20 다에리 사용자 지적) | 하늘색 상태바/장면 사이의 짙은 헤더 띠, 상태바만 별도 덧칠, 불필요한 빈 띠·굵은 선·중복 테두리/그림자, 상단 아래 본문 가림, 제품별 좌표/보정 여백 덧대기 |
|
|
89
89
|
| 레이아웃 바깥 여백·본문 폭 | `ScreenLayout`·부모 콘텐츠 영역의 좌우 바깥 여백은 항상 같은 값, 상하 바깥 여백도 같은 기준. 같은 영역의 카드·입력·목록·버튼 묶음은 동일 폭·경계. 카드 내부 내용·padding은 해당 recipe 유지(2026-10-19 사용자 명확화) | 서로 다른 본문 폭, 한쪽 margin·고정 폭 왼쪽 몰림으로 생긴 바깥 여백 불일치 |
|
|
@@ -104,7 +104,7 @@ import { AlertDialog } from "@hjmds/react-native/overlays";
|
|
|
104
104
|
| 항목 | 값 | 근거 |
|
|
105
105
|
| --- | --- | --- |
|
|
106
106
|
| 크기 | Web `size` `medium` `min(36rem, 100%)` · `small` 28rem · `large` 48rem, 최대 높이 `100dvh − 32`. Native는 폭 100%, 최대 폭은 `small` 320 · `medium` 420 · `large` 640이며 기본은 `small`이다. 아이콘 원 44(`control.minTouchTarget`). Native 행동 버튼 최소 폭 96 | `.hjm-alert-dialog`, `alertDialogRecipe`·`dialogRecipe.sizes`, `react-native/src/overlays.tsx` |
|
|
107
|
-
| 간격 | 화면 가장자리와 최소 `spacing.md` 16. 안쪽 여백은 Native `small` `spacing.lg` 20, `medium`·`large` `spacing.xl` 24이다. 요소 사이 Web `spacing.sm` 12 · Native `dialogRecipe.content.gap` `spacing.md` 16. 행동 사이 `spacing.
|
|
107
|
+
| 간격 | 화면 가장자리와 최소 `spacing.md` 16. 안쪽 여백은 Native `small` `spacing.lg` 20, `medium`·`large` `spacing.xl` 24이다. 요소 사이 Web `spacing.sm` 12 · Native `dialogRecipe.content.gap` `spacing.md` 16. 행동 사이 `spacing.xs` 8, 세로로 쌓이면 `spacing.xs` 8. Native 버튼 좌우 `spacing.md` 16 | `.hjm-overlay`, `.hjm-alert-dialog__actions`, `alertDialogRecipe.actions` |
|
|
108
108
|
| 순서·정렬 | 화면 가운데. 위→아래 [아이콘] → 제목 → 설명 → 오류 문구 → 행동. 행동은 끝 정렬 한 줄 [취소][확인]. 버튼 수는 `mode`가 정한다(alert 1 · confirm 2) | `.hjm-overlay`, `alertDialogRecipe.slots` |
|
|
109
109
|
| 고정·스크롤 | 배경막(`backdrop.modal`)이 화면 전체를 덮고 하단 고정 바보다 위에 쌓인다(Web z-index `layer.modal` 900). 내용이 길면 대화상자 안에서 스크롤한다 | `.hjm-overlay`, `.hjm-alert-dialog` |
|
|
110
110
|
| 좁은 폭·큰 글자 | 폭 < 600(`breakpoint.medium`)이면 행동을 세로로 쌓고 **확인이 위**에 온다(Web DOM은 [취소][확인] 유지). Native는 글자 배율 1.6 이상(`largeTextThreshold`)에서도 쌓는다 | `alertDialogRecipe.actions.stackBelow`·`stackedOrder`, `react-native/src/overlays.tsx` |
|
|
@@ -71,7 +71,7 @@ import { AuthProviderButton } from "@hjmds/react-native/provider-button";
|
|
|
71
71
|
| 항목 | 값 | 근거 |
|
|
72
72
|
| --- | --- | --- |
|
|
73
73
|
| 크기 | 폭을 꽉 채운다(Web `inline-size: 100%`). 최소 높이 44(`buttonRecipe` medium과 `control.minTouchTarget` 중 큰 쪽), 모서리 `radius.md` 12, 로고 20×20, 라벨 `typography.body` 14/20(제공자 가이드가 라벨·색 표현을 소유한다. 네이버 녹색 대비는 [제공자 색 예외](../../provider-button.md#제공자-색과-글자-대비)) | `authProviderButtonRecipe`, `.hjm-auth-provider-button` |
|
|
74
|
-
| 간격 | 좌우 여백 `spacing.md` 16, 로고↔이름 `spacing.sm` 12. 버튼 사이는
|
|
74
|
+
| 간격 | 좌우 여백 `spacing.md` 16, 로고↔이름 `spacing.sm` 12. 버튼 사이는 `Stack gap="xs"`(8) | `authProviderButtonRecipe`, `showcase/web/src/patterns/Agreement.stories.tsx`(`AuthScreenLayoutPreview`) |
|
|
75
75
|
| 순서·정렬 | [AuthScreenLayout](auth-screen-layout.md) `main` 슬롯에 세로로 쌓는다. 안쪽은 [로고]+[제공자 이름]이 가운데 정렬. 순서는 제품이 정하고, 일반 Button(개발 로그인 등)은 제공자 사이가 아니라 목록 아래에 `tone="secondary"`로 둔다 | `.hjm-auth-provider-button`(`justify-content: center`) |
|
|
76
76
|
| 고정·스크롤 | 고정되지 않는다. 진행 중에는 버튼 목록을 그대로 두고 카드 가운데에 로딩 하나만 보인다(`pendingLabel`). 버튼 자리·크기는 바뀌지 않는다 | `react/src/auth-screen.tsx`, `react-native/src/auth-screen.tsx` |
|
|
77
77
|
| 좁은 폭·큰 글자 | 이름이 줄바꿈되고 버튼 높이가 늘어난다. 자르지 않는다 | `.hjm-auth-provider-button__label`(`overflow-wrap: anywhere`) |
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
- 단계: 컴포넌트
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
|
-
- 적용: 1.12.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
6
|
+
- 적용: 1.12.1
|
|
7
|
+
- 검토일: 2026-10-20
|
|
8
8
|
- 근거: [로그인 화면 골격](../../auth-screen.md), [1.4 제품 채택 가이드](../../product-adoption-1.4.md), recipe `authScreenRecipe`(`src/auth-screen.ts`), 포트폴리오 상위 기준 루트 `docs/LOGIN_SCREEN_STANDARD.md`(LS)
|
|
9
9
|
- 스토리북: `배포/컴포넌트/레이아웃/로그인 화면`
|
|
10
10
|
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
`AuthScreenLayout`은 배치를 소유하고, 히어로 문구와 동의 고지·정책 링크는 같은 모듈의 `AuthScreenHero`·`AuthScreenFooter`가
|
|
16
16
|
글자 단계·색·간격을 소유한다. 제품은 문자열·마크·링크 목적지만 넘긴다.
|
|
17
17
|
|
|
18
|
+
기본 API 제공 기준은 1.12.1이며, Native `safeAreaBottom`과 링크 `target`·`rel`·`accessibilityHint`는 1.28.1부터 지원한다.
|
|
19
|
+
|
|
18
20
|
## 쓰지 않을 때
|
|
19
21
|
|
|
20
22
|
| 상황 | 대신 쓸 것 |
|
|
@@ -95,7 +95,7 @@ onClick에서 requestSubmit을 흉내 내거나 빈 콜백을 넣을 필요가
|
|
|
95
95
|
| 항목 | 값 | 근거 |
|
|
96
96
|
| --- | --- | --- |
|
|
97
97
|
| 크기 | 화면 폭을 꽉 채우는 하단 바. 최소 높이 64(Native는 64 + `safeAreaBottom`). 행동 버튼은 칸을 꽉 채우고 높이는 Button `size`를 따른다(기본 medium 44) | `bottomCtaRecipe.minHeight`, `.hjm-bottom-cta__actions .hjm-button`, `react-native/src/actions.tsx` |
|
|
98
|
-
| 간격 | 좌우 `spacing.md` 16으로 ScreenLayout 본문 경계와 일치(2026-10-20 비행중 화면 리뷰에서 독립 하단 바만 4pt 더 들어간 문제 수정·미게시), 위 `spacing.sm` 12, 아래 `spacing.sm` 12와 하단 inset 중 큰 값(Web은 `env(safe-area-inset-bottom)`·`safeAreaBottom`도 비교).
|
|
98
|
+
| 간격 | 좌우 `spacing.md` 16으로 ScreenLayout 본문 경계와 일치(2026-10-20 비행중 화면 리뷰에서 독립 하단 바만 4pt 더 들어간 문제 수정·미게시), 위 `spacing.sm` 12, 아래 `spacing.sm` 12와 하단 inset 중 큰 값(Web은 `env(safe-area-inset-bottom)`·`safeAreaBottom`도 비교). 설명↔행동 `spacing.sm` 12, 행동 사이 `spacing.xs` 8. 위 경계는 `border.default` 1px(`stroke.default`), Native는 위로 드리우는 그림자(opacity 0.08, radius 8) | `bottomCtaRecipe`, `.hjm-bottom-cta` |
|
|
99
99
|
| 순서·정렬 | 위→아래 [설명 한 줄(caption, muted)] → 행동 줄. 행동 줄은 [보조][주] 순서로 같은 폭을 나눈다. 주 행동은 하나다 | `react/src/bottom-cta.tsx`, `react-native/src/actions.tsx` |
|
|
100
100
|
| 고정·스크롤 | 본문 스크롤 영역 아래에 붙는다. Web은 `flow`(문서 흐름 끝) 또는 `sticky`(아래 0에 붙음, z-index `layer.sticky` 100). Native는 제품 화면 레이아웃이 스크롤 영역 밖 아래에 둔다. 키보드가 열리는 폼은 `KeyboardAvoiding` 또는 `KeyboardDock`으로 감싼다 | `.hjm-bottom-cta[data-position="sticky"]`, `react-native/src/keyboard-controller.tsx` |
|
|
101
101
|
| `ScreenLayout` `footer` 안 | footer가 위 경계·`screenPatternRecipe.padding` 16·바탕·safe area를 맡으므로 BottomCTA는 바(경계·여백·바탕·그림자·최소 높이)를 그리지 않고 설명과 행동 줄만 그린다. 버튼 아래 여백은 `max(footer 아래 여백, safeAreaBottom)`이다 — 직접 넘긴 `safeAreaBottom`은 footer가 이미 준 여백을 넘는 만큼만 더한다(16 + 34가 아니라 34). Native는 화면 inset을 ScreenLayout `safeAreaBottom`으로 넘기는 것이 기본이다. 2026-10-17 화면 리뷰: footer 안의 BottomCTA가 경계를 두 겹 긋고 버튼을 한 번 더 들였으며 Native는 footer 안에 그림자 있는 흰 상자를 하나 더 그렸다 | 내부 `ScreenFooterContext`, Web `.hjm-bottom-cta[data-placement="screen-footer"]`, Native `BottomCTA` |
|
|
@@ -83,6 +83,12 @@ import { Button } from "@hjmds/react-native/actions";
|
|
|
83
83
|
| `InlineConfirm` | `label`·`prompt`·`confirmLabel`·`cancelLabel`·`pendingLabel`·`successLabel`·`errorLabel`(모두 `string`), `onConfirm: () => void \| Promise<void>` | — | danger 버튼 → 같은 자리 확인 묶음. Promise가 거부되면 `errorLabel`을 alert로 보인다. Web만 `layoutStyle` |
|
|
84
84
|
| `ReactionPicker` | `label: string`, `options: readonly { id; emoji; label; count?; disabled? }[]`, `value: string \| null`, `onValueChange: (value: string \| null) => void`, `layout?: "wrap" \| "strip"`, `more?: { label; options }` | `layout` `wrap` | 제어 전용. 같은 반응을 다시 누르면 `null`. 이모지는 `strip`(대화 반응 줄)에서만 `typography.title` 18/26으로 커지고 `wrap`은 버튼 글자 크기 그대로다. `more`를 주면 끝에 `+` 토글이 생겨 카탈로그를 펼치고, 카탈로그에만 있는 이모지를 고르면 접히면서 Web 키보드 포커스가 `+` 버튼으로 간다. Web만 `layoutStyle`(루트 기본 `minWidth`보다 우선). `layout`·`more`·`layoutStyle`은 1.13.0 변경 |
|
|
85
85
|
|
|
86
|
+
### 상단 도구 예외
|
|
87
|
+
|
|
88
|
+
2026-10-21 사용자 승인: 상단 도구 줄은 `size="small" hitSlop={0}`, 버튼 영역 사이 `spacing.xxs` 4를 사용한다. 제품 앱·웹 Provider의 `minimumVisualTarget`은 끈다. 일반 작은 버튼의 기본 확장 4와 본문·하단 행동 간격 8은 유지한다. [TopBar 공통 크기](top-bar.md#상단-도구의-공통-크기)를 따른다.
|
|
89
|
+
|
|
90
|
+
`hitSlop`은 Web에서 0 이상의 px 수치, Native에서 RN `PressableProps['hitSlop']`다. Web은 포인터 확장용 pseudo-element에 반영하고 DOM 속성으로 전달하지 않는다. 생략하면 기존 크기 recipe의 확장을 유지한다.
|
|
91
|
+
|
|
86
92
|
## 배치
|
|
87
93
|
|
|
88
94
|
| 항목 | 값 | 근거 |
|
|
@@ -177,7 +183,7 @@ Button의 loading은 표시와 UI 재실행을 막는 기능이다. 같은 프
|
|
|
177
183
|
|
|
178
184
|
## 근거
|
|
179
185
|
|
|
180
|
-
- 버튼 묶음 간격(세로 8·가로 12): 2026-10-17 사용자 지시 — 세로로 쌓은 하단 버튼의 12 간격이 벌어져 보였다.
|
|
186
|
+
- 과거 버튼 묶음 간격(세로 8·가로 12, 2026-10-20 이후 가로도 8로 대체): 2026-10-17 사용자 지시 — 세로로 쌓은 하단 버튼의 12 간격이 벌어져 보였다.
|
|
181
187
|
- Button은 Promise로 성공을 판정하지 않음: 2026-10-07 [Stateful Button 대조](../../../../../docs/plans/aceternity-interaction-adoption-2026-10-07.md)를 기존 [실행과 복구](../../action-session.md)와 비교했다.
|
|
182
188
|
- `loading` 중앙 스피너·크기 유지: 2026-10-06 독립 검증에서 이 사용자 요구가 사용 지침에 빠져 있음을 확인해 명시했다.
|
|
183
189
|
- `selected` 버튼 나열 금지: 2026-10-16 사용자 지적 — 예제의 선택 버튼 줄이 테두리 없는 글자처럼 보이고 폭이 제각각이었다.
|
|
@@ -80,6 +80,7 @@ Web·Native 모두 기존 `raised` Surface의 테두리·그림자를 재사용
|
|
|
80
80
|
| prop | 값 | 기본값 | 설명 |
|
|
81
81
|
| --- | --- | --- | --- |
|
|
82
82
|
| `tone` | `default` · `raised` · `accent` · `sunken` · `subtle`(Surface tone) | `raised` | 테두리와 공통 raised shadow로 입체감 표시. 평면 카드는 `tone="default"` 지정 |
|
|
83
|
+
| `badgesAlign` | `start` · `center` | `start` | 미게시. 들판 대표 카드처럼 그림 아래의 배지 묶음을 가운데 배치할 때 `center` 사용. 줄바꿈된 행에도 적용 |
|
|
83
84
|
| `bordered` | `boolean` | `true` | Surface 기본 `false`와 다르다 |
|
|
84
85
|
| `padding` | spacing 이름 | `md`(16) | — |
|
|
85
86
|
| `radius` | radius 이름 | `lg`(foundation 16) | 선택한 designProfile의 같은 radius 역할로 frame과 media clip을 함께 변경 |
|
|
@@ -205,7 +206,7 @@ subtree라 질감을 바꾸거나 host가 실패해도 본문/초안은 유지
|
|
|
205
206
|
| 항목 | 값 | 근거 |
|
|
206
207
|
| --- | --- | --- |
|
|
207
208
|
| 크기 | 부모 폭을 채우고 높이는 내용이 정한다. 모서리 `radius.lg` 16, 테두리 기본 있음. `media` 자식은 폭 100%(비율은 [AspectRatio](aspect-ratio.md)로) | `cardRecipe.defaults`, `.hjm-card__media > *` |
|
|
208
|
-
| 간격 | 본문 안쪽 `spacing.md` 16, 본문 요소 사이 `spacing.xs` 8, leading↔제목 `spacing.sm` 12. 행동 영역은 좌우·아래 `spacing.md` 16, 버튼 사이 `spacing.
|
|
209
|
+
| 간격 | 본문 안쪽 `spacing.md` 16, 본문 요소 사이 `spacing.xs` 8, leading↔제목 `spacing.sm` 12. 행동 영역은 좌우·아래 `spacing.md` 16, 버튼 사이 `spacing.xs` 8. 카드 목록 사이 간격은 부모 [Stack](stack.md)·[Grid](grid.md)이 정한다 | `cardRecipe.body`·`header`·`actions`, `.hjm-card*` |
|
|
209
210
|
| 순서·정렬 | 위→아래 `media` → [`leading` + `title`/`description`] → `children` → `actions` 고정. leading은 시작 쪽 위 정렬. 행동은 시작 쪽부터 한 줄로 놓인다 | `cardRecipe.slots`, `.hjm-card__header`·`__actions` |
|
|
210
211
|
| 고정·스크롤 | 고정 영역이 없다. 카드 안에서 스크롤 영역을 만들지 않는다(넘치면 `overflow: hidden`으로 잘린다) | `.hjm-card { overflow: hidden }` |
|
|
211
212
|
| 좁은 폭·큰 글자 | 제목·설명이 줄바꿈되고, 행동은 줄바꿈된다(`flex-wrap: wrap`) | `.hjm-card__title`, `.hjm-card__actions`, `react-native/src/data-display.tsx` |
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.17.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-20
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screens.tsx`·`src/screen-flows.tsx`; 기존 개별 지침을 새 규격으로 통합. 예제 스토리는 2026-10-06 사용자 승인으로 스토리북 배포([승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정)). 스토리북 배포는 API 게시가 아니다(`적용` 참고)
|
|
9
|
-
- 스토리북: `배포/구성/정보 표시/대화 메시지`, `배포/화면/소통/채팅`
|
|
9
|
+
- 스토리북: `배포/컴포넌트/데이터 표시/채팅 말풍선`, `배포/구성/정보 표시/대화 메시지`, `배포/화면/소통/채팅`
|
|
10
10
|
|
|
11
11
|
## 언제 쓰나
|
|
12
12
|
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.17.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-20
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screens.tsx`·`src/screen-flows.tsx`; 기존 개별 지침을 새 규격으로 통합. 예제 스토리는 2026-10-06 사용자 승인으로 스토리북 배포([승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정)). 스토리북 배포는 API 게시가 아니다(`적용` 참고)
|
|
9
|
-
- 스토리북: `배포/화면/소통/채팅`
|
|
9
|
+
- 스토리북: `배포/컴포넌트/레이아웃/채팅 화면`, `배포/화면/소통/채팅`
|
|
10
10
|
|
|
11
11
|
## 언제 쓰나
|
|
12
12
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# CommentThreadRow
|
|
2
2
|
|
|
3
3
|
- 단계: 컴포넌트
|
|
4
|
-
- 상태:
|
|
4
|
+
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.23.0
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-20
|
|
8
8
|
- 근거: `src/screen-patterns.ts`의 `commentThreadRecipe`, 양 renderer `src/screen-flows.tsx`의 `CommentThreadRow`·`CommentThreadScreen`
|
|
9
|
-
- 스토리북:
|
|
9
|
+
- 스토리북: `배포/컴포넌트/데이터 표시/댓글 행`
|
|
10
10
|
|
|
11
11
|
## 언제 쓰나
|
|
12
12
|
|
|
13
13
|
가상화 목록(FlatList·가상 스크롤러)처럼 제품이 댓글 목록을 직접 그려야 할 때 댓글 한 행을 그린다.
|
|
14
14
|
`CommentThreadScreen`이 쓰는 것과 같은 행이라 아바타·작성자·본문·답글·좋아요 배치가 화면과 똑같다.
|
|
15
|
-
1.23.0에서 가상화 댓글 목록용으로 공개했다.
|
|
16
|
-
`CommentThreadScreen`
|
|
15
|
+
1.23.0에서 가상화 댓글 목록용으로 공개했다. `배포/컴포넌트/데이터 표시/댓글 행`에서 단독 예제를,
|
|
16
|
+
`배포/화면/소통/댓글`에서 같은 행을 사용하는 `CommentThreadScreen` 예제를 확인한다.
|
|
17
17
|
|
|
18
18
|
## 쓰지 않을 때
|
|
19
19
|
|
|
@@ -67,6 +67,7 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
|
|
|
67
67
|
| `timePlacement` | `"auto"` | 미게시. `"auto"`는 `bodyText`가 없으면 시각을 작성자 옆에 둔다. `"actions"`는 글이 아닌 본문(꽃 도장·카드)에서도 시각을 본문 아래 반응 요약·답글과 같은 줄 맨 앞으로 옮긴다 |
|
|
68
68
|
| `panel` | 없음 | 미게시. 댓글별 인라인 패널(수정·신고·차단 양식). 답글 줄 아래, 길게 누르기 대상 밖에 `panelGap` 8 간격으로 놓인다. 열림 상태·초점·제출·닫기는 제품이 소유한다 |
|
|
69
69
|
| `mediaAlbum` | 없음 | 미게시. 한 장씩 넘기는 사진 묶음 `{ count, renderPage(index), pageLabel(index, count), previousLabel, nextLabel, aspectRatio?, onPress?(index), index?·onIndexChange?·defaultIndex? }`. `renderPage`는 화면에 보이는 장에만 호출되어 제품이 그 사진만 조회·디코딩한다. 불러오는 중·실패 장은 `MediaAlbumPageStatus`(`@hjmds/react(-native)/screens`, [ChatMessage](chat-message.md#축과-기본값))로 그려 안쪽 내용이 낭독기에 닿게 한다. `media`와 함께 주면 `TypeError`. 본문 아래, 길게 누르기 대상 밖에 놓인다 |
|
|
70
|
+
| `authorBadge` | 없음 | 미게시. 닉네임 바로 뒤, 같은 줄에 놓는 제품 배지. 간격 `spacing.xxs` 4, 좁은 폭에서는 줄바꿈한다. `bodyText` 모드에서도 작성자·배지·본문 순서로 배치한다. 배지 문구·노출 조건은 제품이 소유한다 |
|
|
70
71
|
| `avatar` | 없음 | 제품 아바타. 최상위 32·답글 24 폭 칸에 둔다 |
|
|
71
72
|
| `canReply` | `parentId === null` | 답글 행동 표시 여부 |
|
|
72
73
|
| `replyDisabled` | false | 답글 행동 비활성 |
|
|
@@ -79,9 +80,9 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
|
|
|
79
80
|
| 항목 | 값 | 근거 |
|
|
80
81
|
| --- | --- | --- |
|
|
81
82
|
| 크기 | 아바타 칸 최상위 32·답글 24, 좋아요 수 칸 폭 44, `media`·`mediaAlbum` 최대 폭 160·radius `radius.md`(앨범은 사진 칸에만 radius, 그 아래 이전 아이콘 · 위치(예: 2/5) · 다음 아이콘 줄, 작은 ghost IconButton) | `commentThreadRecipe`, `mediaAlbumRecipe` |
|
|
82
|
-
| 간격 | 아바타·본문·좋아요 열 사이 `spacing.xs` 8,
|
|
83
|
+
| 간격 | 아바타·본문·좋아요 열 사이 `spacing.xs` 8, 작성자·본문·행동 줄 사이 `lineGap` = `spacing.xxs` 4, 행동 줄 항목 사이 `spacing.sm` 12, 작성자–시각 `spacing.xxs` 4, `media` 위 4 | `commentThreadRecipe.lineGap`, `Stack gap` |
|
|
83
84
|
| 순서·정렬 | 아바타 → (작성자·시각 → 본문 → media 또는 mediaAlbum → 시각/반응 요약/답글 행동 → panel) → 좋아요 버튼·`actions` 아래 좋아요 수. 위 정렬 단, 오른쪽 좋아요·`actions`·더보기 열은 44 터치 영역을 첫 줄(작성자 caption, `bodyText`면 본문 줄)의 가운데에 맞춘다: 열을 (첫 줄 높이 − 44) / 2만큼 올린다(`commentThreadRecipe.actionAnchor`). HJM의 좋아요·더보기가 마지막이면 열 전체가 IconButton 가장자리 오버행(10)만큼 끝으로 나가 아이콘 칸이 행 끝 여백 선(아바타 시작과 같은 선)에 온다. 제품 `actions`가 마지막이면 그 버튼에 `edge="end"`를 준다. 측정 전후(Web 402): 아바타 168 / 작성자 줄 162 / 아이콘 174 → 아이콘 160 = 작성자 줄, 더보기 칸 끝 28 → 16(2026-10-17 실측 감사, [IconButton `edge`](icon-button.md#배치)) | 양 renderer |
|
|
84
|
-
| 고정·스크롤 | 행은 스크롤하지 않는다. 목록 스크롤·행 간격(`rowGap`
|
|
85
|
+
| 고정·스크롤 | 행은 스크롤하지 않는다. 목록 스크롤·행 간격(`rowGap` = `spacing.lg` 20)·답글 들여쓰기(`replyIndent` 40)는 host가 준다 | `CommentThreadScreen` |
|
|
85
86
|
| 좁은 폭·큰 글자 | 본문 열이 `min-width: 0`으로 줄며 줄바꿈되고 작성자·행동 줄은 `flexWrap`. 좋아요 열은 줄지 않는다 | `.hjm-comment-row__text`, `flexShrink: 0` |
|
|
86
87
|
|
|
87
88
|
좋아요·더보기 버튼 사이의 추가 간격은 0이다. 각 IconButton의 터치 영역은 유지한다.
|
|
@@ -102,6 +103,9 @@ import { CommentThreadRow } from "@hjmds/react-native/screen-flows";
|
|
|
102
103
|
|
|
103
104
|
## 근거
|
|
104
105
|
|
|
106
|
+
- 2026-10-20 Spint의 “나” 배지가 본문 첫 줄을 차지하던 문제를 해결하기 위해 `authorBadge` 슬롯을 추가했다. 작성자에 붙는 표시는 HJM이 닉네임 옆에 배치하고 제품은 배지 내용만 공급한다. 개발 소스 변경이며 미게시다.
|
|
107
|
+
|
|
108
|
+
- 2026-10-20 Spint 댓글이 촘촘하다는 사용자 피드백에 따라 공통 행 사이 간격을 12→20, 내부 줄 사이 간격을 2→4로 조정했다. Web·Native가 같은 recipe를 사용한다. 개발 소스 변경이며 미게시다.
|
|
105
109
|
- `timePlacement`·`panel`·`mediaAlbum`: 2026-10-18 HJM 1.30.0 채택 감사. Spint는 꽃 도장 본문에서 시각을 답글 줄에 두려고 시각·답글 행을 본문 안에 다시 그렸고, Utilverse는 수정·신고·차단 양식을 본문 노드 안(길게 누르기 대상 안)에 넣었으며, 사진 묶음은 선택한 장만 조회하는 제품 앨범을 따로 만들었다.
|
|
106
110
|
|
|
107
111
|
## 플랫폼 차이
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
- 상태: 배포
|
|
5
5
|
- 지원: Web · Native
|
|
6
6
|
- 적용: 1.17.1
|
|
7
|
-
- 검토일: 2026-10-
|
|
7
|
+
- 검토일: 2026-10-20
|
|
8
8
|
- 근거: [반복 화면 조합](../../screen-patterns.md), Web·Native `src/screens.tsx`·`src/screen-flows.tsx`; 기존 개별 지침을 새 규격으로 통합. 예제 스토리는 2026-10-06 사용자 승인으로 스토리북 배포([승인 기록](../../../../../docs/STORYBOOK_NAVIGATION.md#21-2026-10-06-전체-승격과-규격-확정)). 스토리북 배포는 API 게시가 아니다(`적용` 참고)
|
|
9
|
-
- 스토리북: `배포/화면/소통/댓글`
|
|
9
|
+
- 스토리북: `배포/컴포넌트/레이아웃/댓글 화면`, `배포/화면/소통/댓글`
|
|
10
10
|
|
|
11
11
|
## 언제 쓰나
|
|
12
12
|
|
|
@@ -121,7 +121,7 @@ threadHeader도 댓글과 함께 숨겨지므로 항상 보여야 하는 안내
|
|
|
121
121
|
| 항목 | 값 | 근거 |
|
|
122
122
|
| --- | --- | --- |
|
|
123
123
|
| 크기 | ScreenLayout 폭(최대 720); 아바타 `commentThreadRecipe.avatarSize` 32, 답글 아바타 `replyAvatarSize` 24(제품은 이 크기로 넘긴다); 본문 열이 남은 폭을 채우고(`flex: 1`, 최소 폭 0) 오른쪽 끝에 좋아요(44 터치 영역)·`actions`를 한 행으로, 좋아요 수는 하트 아래 가운데; 답글·펼침은 흐린 글자 버튼(caption, 굵게) | `commentThreadRecipe`, Web·Native `CommentThreadRow` |
|
|
124
|
-
| 간격 | 화면 padding `spacing.md` 16; 댓글 행 사이 `rowGap`
|
|
124
|
+
| 간격 | 화면 padding `spacing.md` 16; 댓글 행 사이 `rowGap` = `spacing.lg` 20(최상위·답글 동일); 답글 묶음은 부모 글 열에 맞춰 `replyIndent` 40 들여쓰기; 아바타–본문 `avatarGap` 8; 작성자·본문·답글 줄 사이 `lineGap` = `spacing.xxs` 4; 작성자–시각 `spacing.xxs` 4; 좋아요–actions 추가 간격 0 | `commentThreadRecipe`, [변경 근거](comment-thread-row.md#근거) |
|
|
125
125
|
| 순서·정렬 | 최상위 댓글(아바타 → 작성자·시각 → 본문 → 답글 달기) + 행 오른쪽 좋아요 → `actions` → `commentMenu` 세로 점 버튼(끝), 그 아래 좋아요 수 → 답글 펼침 → 펼친 repliesNotice → 답글 → repliesFooter → … → `threadFooter` → composer(footer) | 렌더 순서 |
|
|
126
126
|
| 고정·스크롤 | 헤더·composer 고정, 댓글은 본문 화면 스크롤(`scroll` 기본 `"screen"`); composer는 `ready`·`empty`에서만 | `ScreenLayout`, `screen-flows.tsx` |
|
|
127
127
|
| 좁은 폭·큰 글자 | 시각·좋아요 수·답글 버튼 줄과 작성자 줄은 줄바꿈(`flexWrap: "wrap"`); 답글은 한 단계만 들여써 좁은 폭에서도 본문 폭을 지킨다 | `screen-flows.tsx` |
|