# AppBar URL: /lynx/components/app-bar Source: https://github.com/daangn/seed-design/blob/dev/docs/content/lynx/components/app-bar.mdx 화면 상단에서 현재 화면의 제목과 탐색 액션을 보여주는 내비게이션 바 컴포넌트입니다. Lynx Engine 최소 버전: 3.6 사용 가능 버전: @seed-design/lynx-react@0.2.0, @seed-design/lynx-css@0.2.0 ## Preview ```tsx import "./styles"; import IconBellLine from "@karrotmarket/lynx-monochrome-icon/IconBellLine"; import { useSeedClassName } from "@seed-design/lynx-react"; import { AppBar, AppBarBackButton, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, } from "@/components/ui/app-bar"; export default function Example() { const seedClassName = useSeedClassName({ colorMode: "system" }); return ( } /> 화면 콘텐츠 ); } ``` ## Installation - npm: npx @seed-design/cli add ui:app-bar - pnpm: pnpm dlx @seed-design/cli add ui:app-bar - yarn: yarn dlx @seed-design/cli add ui:app-bar - bun: bun x @seed-design/cli add ui:app-bar ## Props ### `AppBar` ### `AppBarLeft` ### `AppBarMain` ### `AppBarRight` ### `AppBarSlot` ### `AppBarIconButton` ### `AppBarBackButton` ### `AppBarCloseButton` ## Usage 설치한 snippet은 Root와 주요 슬롯을 `AppBar`, `AppBarLeft`, `AppBarMain`, `AppBarRight`로 export 합니다. `AppBarBackButton`, `AppBarCloseButton`은 기본 아이콘만 제공하며, 실제 이동 로직은 `bindtap`으로 직접 전달합니다. ```tsx import { AppBar, AppBarBackButton, AppBarLeft, AppBarMain, AppBarRight, } from "@/components/ui/app-bar"; export function Header({ goBack }: { goBack: () => void }) { return ( ); } ``` ### Title / Subtitle `AppBarMain`에 `title`, `subtitle`을 전달하면 내부에서 `AppBar.Title`, `AppBar.Subtitle`을 조립합니다. `subtitle`이 있으면 `layout="withSubtitle"`가 자동으로 적용됩니다. ```tsx import "./styles"; import { useSeedClassName } from "@seed-design/lynx-react"; import { AppBar, AppBarMain } from "@/components/ui/app-bar"; export default function Example() { const seedClassName = useSeedClassName({ colorMode: "system" }); return ( 제목과 부제목을 함께 표시한 AppBar ); } ``` 직접 슬롯을 조립하고 싶다면 `AppBarMain`의 children으로 compound 컴포넌트를 전달합니다. ```tsx import { AppBar, AppBarMain } from "@/components/ui/app-bar"; import { AppBar as SeedAppBar } from "@seed-design/lynx-react"; export function Header() { return ( 제목 부제목 ); } ``` ### Left / Right Action 왼쪽과 오른쪽에는 icon button 또는 custom slot을 배치할 수 있습니다. Cupertino theme에서는 좌우 슬롯 폭을 읽어 가운데 title이 치우치지 않도록 padding을 보정합니다. `AppBarLeft`와 `AppBarRight` 모두 첫 번째 자식이 아이콘 버튼이면 왼쪽, 마지막 자식이 아이콘 버튼이면 오른쪽 여백을 보정합니다. 버튼 하나만 있으면 양쪽을 모두 보정합니다. 44px 터치 영역과 24px 아이콘 사이의 여백만큼 음수 margin을 적용해, 터치 영역을 유지하면서 화면 가장자리와 제목 쪽 간격을 React의 슬롯 보정 규칙에 맞춥니다. 슬롯이 가장자리 위치를 Context로 전달하므로 `AppBarBackButton` / `AppBarCloseButton`과 직접 만든 `AppBarIconButton`에 자동으로 적용됩니다. Fragment와 조건부 자식을 지원하며, `AppBarSlot`이나 native 레이아웃 박스 내부에는 자동 보정을 전달하지 않습니다. `edge="leading" | "trailing" | "both"`를 명시하면 자동 보정 방향을 덮어쓸 수 있습니다. ```tsx import "./styles"; import { useState } from "@lynx-js/react"; import { useSeedClassName } from "@seed-design/lynx-react"; import { AppBar, AppBarBackButton, AppBarCloseButton, AppBarLeft, AppBarMain, AppBarRight, AppBarSlot, } from "@/components/ui/app-bar"; export default function Example() { const seedClassName = useSeedClassName({ colorMode: "system" }); const [lastAction, setLastAction] = useState("없음"); function handleBack() { "background only"; setLastAction("뒤로"); } function handleClose() { "background only"; setLastAction("닫기"); } return ( 완료 마지막 액션: {lastAction} ); } ``` `AppBarBackButton` / `AppBarCloseButton` 외에 다른 아이콘을 쓰고 싶다면 `AppBarIconButton`의 `icon` prop에 Lynx 아이콘을 전달해 직접 구성합니다. 가장자리에 놓이면 위와 동일하게 자동으로 정렬됩니다. ```tsx import IconBellLine from "@karrotmarket/lynx-monochrome-icon/IconBellLine"; import { AppBar, AppBarBackButton, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, } from "@/components/ui/app-bar"; export function Header({ goBack }: { goBack: () => void }) { return ( } /> ); } ``` ### Multiple / Conditional Actions 슬롯에 여러 버튼을 넣으면 첫 번째와 마지막 버튼에만 가장자리 여백 보정이 적용됩니다. 가운데 버튼의 터치 영역은 그대로 유지됩니다. 조건부 렌더링으로 버튼이 하나만 남으면 양쪽 여백 보정으로 자동 변경됩니다. 아래 예제에서 검색·닫기를 숨기거나 다시 표시해도 제목은 가운데에 유지됩니다. 각 아이콘 버튼을 눌러 마지막 액션도 확인할 수 있습니다. ```tsx import "./styles"; import IconBellLine from "@karrotmarket/lynx-monochrome-icon/IconBellLine"; import IconMagnifyingglassLine from "@karrotmarket/lynx-monochrome-icon/IconMagnifyingglassLine"; import { useState } from "@lynx-js/react"; import { ActionButton, useSeedClassName } from "@seed-design/lynx-react"; import { AppBar, AppBarBackButton, AppBarCloseButton, AppBarIconButton, AppBarLeft, AppBarMain, AppBarRight, } from "@/components/ui/app-bar"; export default function Example() { const seedClassName = useSeedClassName({ colorMode: "system" }); const [showExtraActions, setShowExtraActions] = useState(true); const [lastAction, setLastAction] = useState("없음"); function toggleExtraActions() { "background only"; setShowExtraActions((value) => !value); } function recordAction(action: string) { "background only"; setLastAction(action); } return ( recordAction("뒤로")} /> } bindtap={() => recordAction("알림")} /> {showExtraActions && ( <> } bindtap={() => recordAction("검색")} /> recordAction("닫기")} /> )} {showExtraActions ? "검색·닫기 숨기기" : "검색·닫기 표시"} 마지막 액션: {lastAction} ); } ``` ### Custom Slot / Explicit Edge 직접 만든 레이아웃 박스나 `AppBarSlot` 안의 버튼에는 가장자리 보정을 자동 적용하지 않습니다. 내부 배치를 직접 관리할 때만 `edge`로 방향을 지정하세요. 일반적인 `AppBarLeft` / `AppBarRight`의 버튼에는 지정할 필요가 없습니다. ```tsx ``` `leading`은 왼쪽, `trailing`은 오른쪽, `both`는 양쪽 여백을 보정합니다. 명시한 값은 자동 보정보다 우선하며, 버튼의 터치 영역 크기는 바뀌지 않습니다. ### Tone 기본값인 `tone="layer"`는 레이어 배경을 표시합니다. `tone="transparent"`는 배경을 투명하게 하고 제목과 아이콘에 밝은 색상을 적용하므로 사진이나 어두운 배경 위에서 사용하세요. 배경과 스크롤에 따른 tone 전환은 호출부에서 관리합니다. ```tsx import "./styles"; import { useState } from "@lynx-js/react"; import { useSeedClassName } from "@seed-design/lynx-react"; import { AppBar, AppBarBackButton, AppBarCloseButton, AppBarLeft, AppBarMain, AppBarRight, } from "@/components/ui/app-bar"; export default function Example() { const seedClassName = useSeedClassName({ colorMode: "system" }); const [lastAction, setLastAction] = useState("배경 위에 겹쳐지는 AppBar"); function handleBack() { "background only"; setLastAction("뒤로 버튼을 눌렀습니다"); } function handleClose() { "background only"; setLastAction("닫기 버튼을 눌렀습니다"); } return ( {lastAction} ); } ``` ### Accessibility Lynx에서는 HTML ARIA 속성 대신 Lynx native 접근성 속성을 사용합니다. `AppBarIconButton`은 기본적으로 `accessibility-element={true}`와 `accessibility-traits="button"`을 적용하며, 아이콘만으로 의미를 알 수 없는 버튼에는 `accessibility-label`을 전달해야 합니다. ```tsx import IconBellLine from "@karrotmarket/lynx-monochrome-icon/IconBellLine"; } /> ``` `AppBarBackButton`, `AppBarCloseButton`은 각각 `"뒤로"`, `"닫기"` 라벨을 기본값으로 제공합니다. ### Theme `theme`은 `"cupertino"` 또는 `"android"`를 사용할 수 있습니다. 명시하지 않으면 AppBar 내부에서 `SystemInfo.platform`을 읽어 Android에서는 `"android"`, 그 외 플랫폼이나 값이 없을 때는 `"cupertino"`를 사용합니다. ```tsx ``` 수동으로 지정한 `theme`은 platform 기본값보다 우선합니다. ```tsx import "./styles"; import { useSeedClassName } from "@seed-design/lynx-react"; import { AppBar, AppBarBackButton, AppBarCloseButton, AppBarLeft, AppBarMain, AppBarRight, } from "@/components/ui/app-bar"; export default function Example() { const seedClassName = useSeedClassName({ colorMode: "system" }); return ( Cupertino Android ); } ``` ## Web Version Differences Lynx `AppBar`는 React `AppBar`와 같은 역할과 variant 이름을 제공하지만, 플랫폼별 Recipe와 화면 전환 책임은 다릅니다. - **이벤트 핸들링**: `onClick` 대신 Lynx 이벤트인 `bindtap`을 전달합니다. - **렌더링 요소**: HTML 요소 대신 네이티브 `` / `` 요소를 렌더링합니다. - **접근성 라벨링**: `aria-label` 대신 Lynx native `accessibility-label`을 사용합니다. - **platform theme**: 웹처럼 `data-*` selector에 의존하지 않고, recipe variant className으로 `"cupertino"` / `"android"` 디자인을 적용합니다. - **navigation 책임**: `AppBarBackButton`과 `AppBarCloseButton`은 이동을 자동 실행하지 않습니다. Stackflow `pop()` 같은 동작은 호출부에서 연결합니다. - **가장자리 아이콘 정렬**: 웹은 `iconButton:first-child` / `:last-child` selector로 bleed를 보정하지만, Lynx는 해당 가상 클래스를 지원하지 않아 `AppBarLeft` / `AppBarRight`가 가장자리 위치를 Context로 전달하고 아이콘 버튼의 Recipe class로 보정합니다. 다중 버튼·custom slot에서도 가장자리 아이콘 버튼에만 정확히 적용됩니다. ## Unsupported Lynx Features 현재 Lynx AppBar는 순수 UI 컴포넌트로 제공되며 다음 기능은 포함하지 않습니다. | 기능 | 웹 대응 | 대체 방법 | | --------------------------- | -------------------------------------------- | ------------------------------------------------- | | Stackflow activity 연동 | `useActivity`, `useActions` 기반 back/close 처리 | `bindtap`에서 앱의 navigation 함수를 직접 호출 | | swipe-back 전환 애니메이션 | Stackflow screen transition | 화면 전환을 소유한 navigation 계층에서 처리 | | `data-*` selector 기반 상태 스타일 | 웹 recipe selector | `theme`, `tone` variant className 사용 | | HTML button 속성 | `type`, DOM click event | Lynx `` 기반 `AppBarIconButton`과 `bindtap` 사용 | | HTML ARIA 라벨 | `aria-label` | Lynx native `accessibility-label` 사용 |