feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@d3ro/ui-native",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"description": "D3RO Voice React Native DS — MetalCard/PhosphorText/Led/WaveBars etc.",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
// RN FilterChip — pill-shaped toggle chip for filter bars
|
||||
|
||||
import { Pressable, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeRadius } from '../theme'
|
||||
import { d3roNativeRadius } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
import { PhosphorText } from './PhosphorText'
|
||||
|
||||
export interface FilterChipProps {
|
||||
|
|
@ -18,14 +19,21 @@ export function FilterChip({
|
|||
onPress,
|
||||
style
|
||||
}: FilterChipProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityState={{ selected: active }}
|
||||
accessibilityLabel={label}
|
||||
onPress={onPress}
|
||||
style={[
|
||||
{
|
||||
backgroundColor: active ? d3roNativePalette.accent.main : d3roNativePalette.bg.card,
|
||||
minHeight: 48,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: active ? palette.accent.main : palette.bg.card,
|
||||
borderWidth: 1,
|
||||
borderColor: active ? d3roNativePalette.accent.main : d3roNativePalette.border.default,
|
||||
borderColor: active ? palette.accent.main : palette.border.default,
|
||||
borderRadius: d3roNativeRadius.pill,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 6
|
||||
|
|
@ -36,7 +44,7 @@ export function FilterChip({
|
|||
<PhosphorText
|
||||
variant="small"
|
||||
color={active ? 'primary' : 'muted'}
|
||||
style={active ? { color: d3roNativePalette.bg.app } : undefined}
|
||||
style={active ? { color: palette.text.onAccent ?? palette.bg.app } : undefined}
|
||||
>
|
||||
{label}
|
||||
</PhosphorText>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import { View, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { Led } from './Led'
|
||||
import { PhosphorText } from './PhosphorText'
|
||||
import { d3roNativePalette } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface HeaderProps {
|
||||
title: string
|
||||
|
|
@ -22,6 +22,7 @@ export function Header({
|
|||
paddingTop = 0,
|
||||
style
|
||||
}: HeaderProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
|
|
@ -33,14 +34,18 @@ export function Header({
|
|||
paddingTop: paddingTop + 8,
|
||||
paddingBottom: 12,
|
||||
borderBottomWidth: showBorder ? 1 : 0,
|
||||
borderBottomColor: d3roNativePalette.border.subtle
|
||||
borderBottomColor: palette.border.subtle
|
||||
},
|
||||
style
|
||||
]}
|
||||
>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
||||
<Led color="amber" size={8} />
|
||||
<PhosphorText variant="label" color="muted" style={{ letterSpacing: 3 }}>
|
||||
<PhosphorText
|
||||
accessibilityRole="header"
|
||||
variant="label"
|
||||
color="muted"
|
||||
>
|
||||
{title}
|
||||
</PhosphorText>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// RN용 Led — 작은 발광 원
|
||||
|
||||
import { View, type ViewStyle } from 'react-native'
|
||||
import { d3roNativePalette } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface LedProps {
|
||||
color?: 'amber' | 'green' | 'red' | 'orange' | 'blue'
|
||||
|
|
@ -10,21 +10,21 @@ export interface LedProps {
|
|||
size?: number
|
||||
}
|
||||
|
||||
const COLOR_MAP: Record<string, string> = {
|
||||
amber: d3roNativePalette.accent.main,
|
||||
green: d3roNativePalette.tag.green,
|
||||
red: d3roNativePalette.tag.red,
|
||||
orange: d3roNativePalette.tag.orange,
|
||||
blue: d3roNativePalette.tag.blue
|
||||
}
|
||||
|
||||
export function Led({ color = 'amber', on = true, size = 8 }: LedProps): React.ReactElement {
|
||||
const c = COLOR_MAP[color] ?? d3roNativePalette.accent.main
|
||||
const palette = useNativePalette()
|
||||
const colorMap: Record<NonNullable<LedProps['color']>, string> = {
|
||||
amber: palette.accent.main,
|
||||
green: palette.tag.green,
|
||||
red: palette.tag.red,
|
||||
orange: palette.tag.orange,
|
||||
blue: palette.tag.blue
|
||||
}
|
||||
const c = colorMap[color]
|
||||
const style: ViewStyle = {
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: size / 2,
|
||||
backgroundColor: on ? c : d3roNativePalette.led.off,
|
||||
backgroundColor: on ? c : palette.led.off,
|
||||
shadowColor: c,
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: on ? 0.8 : 0,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
// RN용 MetalCard — View + 섀시 스타일
|
||||
|
||||
import { View, type ViewProps, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeRadius } from '../theme'
|
||||
import { d3roNativeRadius } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface MetalCardProps extends ViewProps {
|
||||
inset?: boolean
|
||||
|
|
@ -10,15 +11,16 @@ export interface MetalCardProps extends ViewProps {
|
|||
}
|
||||
|
||||
export function MetalCard({ children, inset = false, style, ...rest }: MetalCardProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const baseStyle: ViewStyle = {
|
||||
backgroundColor: inset ? d3roNativePalette.bg.inset : d3roNativePalette.bg.card,
|
||||
backgroundColor: inset ? palette.bg.inset : palette.bg.card,
|
||||
borderRadius: inset ? d3roNativeRadius.inner : d3roNativeRadius.card,
|
||||
padding: inset ? 6 : 16,
|
||||
borderWidth: inset ? 0 : 1,
|
||||
borderColor: d3roNativePalette.border.subtle,
|
||||
borderColor: palette.border.subtle,
|
||||
overflow: 'hidden',
|
||||
// RN 그림자
|
||||
shadowColor: '#000',
|
||||
shadowColor: palette.shadow ?? '#000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: inset ? 0 : 0.25,
|
||||
shadowRadius: inset ? 0 : 8,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
// packages/ui-native/src/components/PhosphorText.tsx
|
||||
// RN용 PhosphorText — Text + 앰버 glow (textShadowColor)
|
||||
// RN용 PhosphorText — v3: 시스템 산세리프 기반 (한글은 Apple SD Gothic Neo /
|
||||
// Noto Sans KR 자동 폴백). 텍스트 글로우는 제거하고 명도로 위계를 만든다.
|
||||
|
||||
import { Text, Platform, type TextProps, type TextStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeTypo, type D3roNativeTypoKey } from '../theme'
|
||||
import { Text, type TextProps, type TextStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativeFonts, d3roNativeTypo, type D3roNativeTypoKey } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export type PhosphorVariant = D3roNativeTypoKey
|
||||
|
||||
|
|
@ -19,29 +21,23 @@ export function PhosphorText({
|
|||
style,
|
||||
...rest
|
||||
}: PhosphorTextProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const typo = d3roNativeTypo[variant]
|
||||
const isAmberVariant = variant === 'hero' || variant === 'title' || variant === 'value'
|
||||
|
||||
const resolvedColor: string = (() => {
|
||||
if (color === 'amber') return d3roNativePalette.accent.main
|
||||
if (color === 'primary') return d3roNativePalette.text.primary
|
||||
if (color === 'secondary') return d3roNativePalette.text.secondary
|
||||
if (color === 'label') return d3roNativePalette.text.label
|
||||
if (color === 'muted') return d3roNativePalette.text.muted
|
||||
return isAmberVariant ? d3roNativePalette.accent.main : d3roNativePalette.text.primary
|
||||
if (color === 'amber') return palette.accent.main
|
||||
if (color === 'primary') return palette.text.primary
|
||||
if (color === 'secondary') return palette.text.secondary
|
||||
if (color === 'label') return palette.text.label
|
||||
if (color === 'muted') return palette.text.muted
|
||||
return isAmberVariant ? palette.accent.main : palette.text.primary
|
||||
})()
|
||||
|
||||
const baseStyle: TextStyle = {
|
||||
...typo,
|
||||
color: resolvedColor,
|
||||
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
|
||||
...(isAmberVariant
|
||||
? {
|
||||
textShadowColor: d3roNativePalette.accent.glow,
|
||||
textShadowOffset: { width: 0, height: 0 },
|
||||
textShadowRadius: 6
|
||||
}
|
||||
: {})
|
||||
fontFamily: d3roNativeFonts.sans
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import {
|
|||
type TextStyle,
|
||||
type StyleProp
|
||||
} from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeRadius, d3roNativeTypo } from '../theme'
|
||||
import { d3roNativeRadius, d3roNativeTypo } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface PhysicalButtonProps extends Omit<PressableProps, 'children' | 'style'> {
|
||||
label: string
|
||||
|
|
@ -27,28 +28,30 @@ export function PhysicalButton({
|
|||
onPress,
|
||||
...rest
|
||||
}: PhysicalButtonProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const [pressed, setPressed] = useState(false)
|
||||
|
||||
const bgColor =
|
||||
variant === 'primary'
|
||||
? d3roNativePalette.accent.main
|
||||
? palette.accent.main
|
||||
: variant === 'danger'
|
||||
? d3roNativePalette.tag.red
|
||||
? palette.tag.red
|
||||
: 'transparent'
|
||||
|
||||
const borderColor =
|
||||
variant === 'secondary' ? d3roNativePalette.border.strong : 'transparent'
|
||||
variant === 'secondary' ? palette.border.strong : 'transparent'
|
||||
|
||||
const textColor =
|
||||
variant === 'primary' || variant === 'danger'
|
||||
? d3roNativePalette.text.primary
|
||||
: d3roNativePalette.text.secondary
|
||||
? palette.text.onAccent ?? palette.text.primary
|
||||
: palette.text.secondary
|
||||
|
||||
const containerStyle: ViewStyle = {
|
||||
backgroundColor: bgColor,
|
||||
borderColor,
|
||||
borderWidth: variant === 'secondary' ? 1 : 0,
|
||||
borderRadius: d3roNativeRadius.button,
|
||||
minHeight: 48,
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 24,
|
||||
alignItems: 'center',
|
||||
|
|
@ -59,19 +62,22 @@ export function PhysicalButton({
|
|||
|
||||
const textStyle: TextStyle = {
|
||||
...d3roNativeTypo.heading,
|
||||
color: textColor,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 1.5
|
||||
color: textColor
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
{...rest}
|
||||
accessibilityRole={rest.accessibilityRole ?? 'button'}
|
||||
accessibilityState={{
|
||||
...rest.accessibilityState,
|
||||
disabled,
|
||||
}}
|
||||
onPress={onPress}
|
||||
onPressIn={() => setPressed(true)}
|
||||
onPressOut={() => setPressed(false)}
|
||||
disabled={disabled}
|
||||
style={[containerStyle, style]}
|
||||
{...rest}
|
||||
>
|
||||
<Text style={textStyle}>{label}</Text>
|
||||
</Pressable>
|
||||
|
|
|
|||
|
|
@ -2,15 +2,17 @@
|
|||
// RN ScreenPanel — inset bg + dot grid background
|
||||
|
||||
import { View, type ViewProps, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeRadius } from '../theme'
|
||||
import { d3roNativeRadius } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface ScreenPanelProps extends ViewProps {
|
||||
style?: StyleProp<ViewStyle>
|
||||
}
|
||||
|
||||
export function ScreenPanel({ children, style, ...rest }: ScreenPanelProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const baseStyle: ViewStyle = {
|
||||
backgroundColor: d3roNativePalette.bg.inset,
|
||||
backgroundColor: palette.bg.inset,
|
||||
borderRadius: d3roNativeRadius.inner,
|
||||
padding: 20,
|
||||
overflow: 'hidden'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
// RN StatusBar — bottom status strip with LED + model label
|
||||
|
||||
import { View, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeFonts } from '../theme'
|
||||
import { d3roNativeFonts } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
import { Led } from './Led'
|
||||
import { PhosphorText } from './PhosphorText'
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ export function AppStatusBar({
|
|||
label = 'PRECISION DATA LINK',
|
||||
style
|
||||
}: AppStatusBarProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const containerStyle: ViewStyle = {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
|
|
@ -33,15 +35,15 @@ export function AppStatusBar({
|
|||
<PhosphorText
|
||||
variant="label"
|
||||
color="muted"
|
||||
style={{ fontFamily: d3roNativeFonts.mono, fontSize: 8, letterSpacing: 1.5 }}
|
||||
style={{ fontFamily: d3roNativeFonts.mono, fontSize: 10, letterSpacing: 0.6 }}
|
||||
>
|
||||
{model}
|
||||
</PhosphorText>
|
||||
<View style={{ width: 1, height: 8, backgroundColor: d3roNativePalette.border.default }} />
|
||||
<View style={{ width: 1, height: 8, backgroundColor: palette.border.default }} />
|
||||
<PhosphorText
|
||||
variant="label"
|
||||
color="muted"
|
||||
style={{ fontFamily: d3roNativeFonts.mono, fontSize: 8, letterSpacing: 1.5 }}
|
||||
style={{ fontFamily: d3roNativeFonts.mono, fontSize: 10, letterSpacing: 0.6 }}
|
||||
>
|
||||
{label}
|
||||
</PhosphorText>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
// packages/ui-native/src/components/WaveBars.tsx
|
||||
// RN WaveBars — animated audio level bars using core Animated API
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { View, Animated, Easing, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette } from '../theme'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
AccessibilityInfo,
|
||||
View,
|
||||
Animated,
|
||||
Easing,
|
||||
type ViewStyle,
|
||||
type StyleProp,
|
||||
} from 'react-native'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface WaveBarsProps {
|
||||
active?: boolean
|
||||
barCount?: number
|
||||
reduceMotion?: boolean
|
||||
style?: StyleProp<ViewStyle>
|
||||
}
|
||||
|
||||
|
|
@ -15,7 +23,15 @@ const BAR_MIN = 8
|
|||
const BAR_MAX = 32
|
||||
const DURATIONS = [800, 1000, 900, 1200, 1100, 950, 1050, 1300, 850, 1150, 1000]
|
||||
|
||||
function WaveBar({ index, active }: { index: number; active: boolean }): React.ReactElement {
|
||||
function WaveBar({
|
||||
index,
|
||||
active,
|
||||
color,
|
||||
}: {
|
||||
index: number
|
||||
active: boolean
|
||||
color: string
|
||||
}): React.ReactElement {
|
||||
const height = useRef(new Animated.Value(BAR_MIN)).current
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -41,11 +57,8 @@ function WaveBar({ index, active }: { index: number; active: boolean }): React.R
|
|||
animation.start()
|
||||
return () => animation.stop()
|
||||
} else {
|
||||
Animated.timing(height, {
|
||||
toValue: BAR_MIN,
|
||||
duration: 300,
|
||||
useNativeDriver: false
|
||||
}).start()
|
||||
height.stopAnimation()
|
||||
height.setValue(BAR_MIN)
|
||||
}
|
||||
}, [active, height, index])
|
||||
|
||||
|
|
@ -54,7 +67,7 @@ function WaveBar({ index, active }: { index: number; active: boolean }): React.R
|
|||
style={{
|
||||
width: 6,
|
||||
height,
|
||||
backgroundColor: d3roNativePalette.accent.main,
|
||||
backgroundColor: color,
|
||||
borderRadius: 3
|
||||
}}
|
||||
/>
|
||||
|
|
@ -64,23 +77,54 @@ function WaveBar({ index, active }: { index: number; active: boolean }): React.R
|
|||
export function WaveBars({
|
||||
active = false,
|
||||
barCount = 11,
|
||||
reduceMotion,
|
||||
style
|
||||
}: WaveBarsProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
// Fail closed: do not animate until the asynchronous OS preference is known.
|
||||
const [systemReduceMotion, setSystemReduceMotion] = useState(
|
||||
reduceMotion === undefined,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (reduceMotion !== undefined) return undefined
|
||||
let mounted = true
|
||||
void AccessibilityInfo.isReduceMotionEnabled()
|
||||
.then((enabled) => {
|
||||
if (mounted) setSystemReduceMotion(enabled)
|
||||
})
|
||||
.catch(() => undefined)
|
||||
const subscription = AccessibilityInfo.addEventListener(
|
||||
'reduceMotionChanged',
|
||||
setSystemReduceMotion,
|
||||
)
|
||||
return () => {
|
||||
mounted = false
|
||||
subscription.remove()
|
||||
}
|
||||
}, [reduceMotion])
|
||||
|
||||
const motionDisabled = reduceMotion ?? systemReduceMotion
|
||||
const containerStyle: ViewStyle = {
|
||||
height: 128,
|
||||
backgroundColor: d3roNativePalette.bg.inset,
|
||||
backgroundColor: palette.bg.inset,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 6,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: d3roNativePalette.border.subtle
|
||||
borderBottomColor: palette.border.subtle
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[containerStyle, style]}>
|
||||
{Array.from({ length: barCount }).map((_, i) => (
|
||||
<WaveBar key={i} index={i} active={active} />
|
||||
<WaveBar
|
||||
key={i}
|
||||
index={i}
|
||||
active={active && !motionDisabled}
|
||||
color={palette.accent.main}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,15 @@ export {
|
|||
d3roNativeTypo,
|
||||
d3roNativeRadius,
|
||||
d3roNativeFonts,
|
||||
type D3roNativePalette,
|
||||
type D3roNativePaletteKey,
|
||||
type D3roNativeTypoKey
|
||||
} from './theme'
|
||||
export {
|
||||
NativeThemeProvider,
|
||||
useNativePalette,
|
||||
type NativeThemeProviderProps
|
||||
} from './theme-context'
|
||||
export { MetalCard, type MetalCardProps } from './components/MetalCard'
|
||||
export {
|
||||
PhosphorText,
|
||||
|
|
|
|||
31
packages/ui-native/src/theme-context.tsx
Normal file
31
packages/ui-native/src/theme-context.tsx
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { createContext, useContext, type ReactNode } from 'react'
|
||||
import {
|
||||
d3roNativePalette,
|
||||
type D3roNativePalette,
|
||||
} from './theme'
|
||||
|
||||
const NativeThemeContext = createContext<D3roNativePalette | null>(null)
|
||||
|
||||
export interface NativeThemeProviderProps {
|
||||
palette: D3roNativePalette
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional runtime palette boundary for React Native consumers.
|
||||
* Components outside a provider retain the legacy palette for compatibility.
|
||||
*/
|
||||
export function NativeThemeProvider({
|
||||
palette,
|
||||
children,
|
||||
}: NativeThemeProviderProps): React.ReactElement {
|
||||
return (
|
||||
<NativeThemeContext.Provider value={palette}>
|
||||
{children}
|
||||
</NativeThemeContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function useNativePalette(): D3roNativePalette {
|
||||
return useContext(NativeThemeContext) ?? d3roNativePalette
|
||||
}
|
||||
|
|
@ -4,7 +4,51 @@
|
|||
|
||||
import { Platform } from 'react-native'
|
||||
|
||||
export const d3roNativePalette = {
|
||||
export interface D3roNativePalette {
|
||||
bg: {
|
||||
app: string
|
||||
card: string
|
||||
cardHover: string
|
||||
elevated: string
|
||||
sidebar: string
|
||||
inset: string
|
||||
chassis: string
|
||||
}
|
||||
text: {
|
||||
primary: string
|
||||
white: string
|
||||
onAccent?: string
|
||||
secondary: string
|
||||
label: string
|
||||
disabled: string
|
||||
inactive: string
|
||||
muted: string
|
||||
}
|
||||
accent: {
|
||||
main: string
|
||||
pressed?: string
|
||||
dim: string
|
||||
glow: string
|
||||
green: string
|
||||
greenGlow: string
|
||||
}
|
||||
border: {
|
||||
subtle: string
|
||||
default: string
|
||||
strong: string
|
||||
}
|
||||
tag: {
|
||||
purple: string
|
||||
orange: string
|
||||
red: string
|
||||
green: string
|
||||
blue: string
|
||||
}
|
||||
led: { off: string }
|
||||
shadow?: string
|
||||
}
|
||||
|
||||
export const d3roNativePalette: D3roNativePalette = {
|
||||
bg: {
|
||||
app: '#19191b',
|
||||
card: '#242427',
|
||||
|
|
@ -24,9 +68,10 @@ export const d3roNativePalette = {
|
|||
muted: '#71717a'
|
||||
},
|
||||
accent: {
|
||||
main: '#ff5c35',
|
||||
dim: 'rgba(255, 92, 53, 0.15)',
|
||||
glow: 'rgba(255, 92, 53, 0.6)',
|
||||
// 브랜드 통일: 앱(Midnight Glass) blue — 레거시 amber 폐기
|
||||
main: '#3b82f6',
|
||||
dim: 'rgba(59, 130, 246, 0.15)',
|
||||
glow: 'rgba(59, 130, 246, 0.5)',
|
||||
green: '#4ade80',
|
||||
greenGlow: 'rgba(74, 222, 128, 0.6)'
|
||||
},
|
||||
|
|
@ -47,15 +92,19 @@ export const d3roNativePalette = {
|
|||
}
|
||||
} as const
|
||||
|
||||
// v3 "타이포그래피 퍼스트" — packages/ui d3roTypo v3와 동일 철학.
|
||||
// 볼드(700) 폐기, 크기·행간·명도로 위계. 한글 행간 1.5~1.6,
|
||||
// 자간 0 근처 (양수 트래킹은 라틴 대문자 라벨에만 소량).
|
||||
// RN letterSpacing 단위는 px.
|
||||
export const d3roNativeTypo = {
|
||||
hero: { fontSize: 42, fontWeight: '300' as const, letterSpacing: -2, lineHeight: 42 },
|
||||
title: { fontSize: 28, fontWeight: '300' as const, letterSpacing: -1, lineHeight: 34 },
|
||||
value: { fontSize: 20, fontWeight: '400' as const, letterSpacing: 0.4, lineHeight: 20 },
|
||||
heading: { fontSize: 16, fontWeight: '600' as const, letterSpacing: 0.32, lineHeight: 22 },
|
||||
body: { fontSize: 14, fontWeight: '400' as const, letterSpacing: 0.14, lineHeight: 21 },
|
||||
small: { fontSize: 12, fontWeight: '600' as const, letterSpacing: 0.36, lineHeight: 17 },
|
||||
meta: { fontSize: 11, fontWeight: '600' as const, letterSpacing: 0.55, lineHeight: 14 },
|
||||
label: { fontSize: 10, fontWeight: '700' as const, letterSpacing: 2, lineHeight: 12 }
|
||||
hero: { fontSize: 42, fontWeight: '400' as const, letterSpacing: -0.5, lineHeight: 50 },
|
||||
title: { fontSize: 28, fontWeight: '400' as const, letterSpacing: -0.3, lineHeight: 36 },
|
||||
value: { fontSize: 20, fontWeight: '500' as const, letterSpacing: 0, lineHeight: 26 },
|
||||
heading: { fontSize: 16, fontWeight: '500' as const, letterSpacing: 0, lineHeight: 24 },
|
||||
body: { fontSize: 14, fontWeight: '400' as const, letterSpacing: 0, lineHeight: 22 },
|
||||
small: { fontSize: 12, fontWeight: '400' as const, letterSpacing: 0, lineHeight: 18 },
|
||||
meta: { fontSize: 11, fontWeight: '500' as const, letterSpacing: 0.3, lineHeight: 16 },
|
||||
label: { fontSize: 11, fontWeight: '500' as const, letterSpacing: 0.4, lineHeight: 15 }
|
||||
} as const
|
||||
|
||||
export const d3roNativeRadius = {
|
||||
|
|
@ -66,9 +115,14 @@ export const d3roNativeRadius = {
|
|||
pill: 999
|
||||
} as const
|
||||
|
||||
// 폰트 전략 (bare RN, 커스텀 폰트 번들링 없음):
|
||||
// - sans: 플랫폼 시스템 산세리프. iOS=SF Pro(한글은 Apple SD Gothic Neo로 자동
|
||||
// 폴백), Android=Roboto(한글은 Noto Sans KR로 자동 폴백). Apple HIG/Material
|
||||
// 권장을 따르는 표준 방식이며 한글 렌더링 품질이 가장 안정적.
|
||||
// - mono: 텔레메트리·코드 등 등폭이 필요한 곳에만 사용 (한글엔 사용 금지).
|
||||
export const d3roNativeFonts = {
|
||||
mono: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
|
||||
sans: Platform.OS === 'ios' ? 'System' : 'Roboto'
|
||||
sans: Platform.OS === 'ios' ? 'System' : 'Roboto',
|
||||
mono: Platform.OS === 'ios' ? 'Menlo' : 'monospace'
|
||||
} as const
|
||||
|
||||
export type D3roNativePaletteKey = keyof typeof d3roNativePalette
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue