Mockup | CoUI

Mockup

브라우저, 폰, 코드 에디터 등 디바이스 프레임을 시뮬레이션하는 목업 컴포넌트

Mockup#

콘텐츠를 실제 디바이스나 앱 화면처럼 감싸는 목업 프레임 컴포넌트입니다. 문서, 랜딩 페이지, 스크린샷 등 시각적 프레젠테이션에 활용됩니다.

Live Preview#

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 랜딩 페이지에서 앱/웹 화면을 디바이스 프레임 안에 표시할 때
  • 기술 문서에서 코드 예제를 코드 에디터 스타일로 표시할 때
  • 데모나 쇼케이스에서 스크린샷을 실제 기기처럼 보여줄 때

대신 다른 컴포넌트를 사용하세요:

  • Card: 단순한 콘텐츠 카드 레이아웃에
  • CodeSnippet: 코드 하이라이팅이 필요한 코드 블록에

기본 사용법 (Basic Usage)#

// 브라우저 목업
MockupBrowser(
  addressBar: 'https://coui.cocode.im',
  child: Image.asset('assets/screenshots/dashboard.png'),
)

// 폰 목업
MockupPhone(
  child: Image.asset('assets/screenshots/app_home.png'),
)

// 코드 에디터 목업
MockupCode.fromString('void main() {\n  runApp(const MyApp());\n}')

// 윈도우 목업
MockupWindow(
  child: Text('윈도우 내용'),
)
// 브라우저 목업
MockupBrowser(
  addressBar: 'https://coui.cocode.im',
  child: img(src: 'screenshot.png', alt: 'CoUI 디자인 시스템'),
)

// 폰 목업
MockupPhone(
  child: img(src: 'app-screenshot.png', alt: '앱 화면'),
)

// 코드 목업 (fromString 팩토리 생성자)
MockupCode.fromString(
  'npm i coui\ninstalling...\nDone!',
  showLineNumbers: true,
)

// 윈도우 목업
MockupWindow(
  child: Text('윈도우 내용'),
)

Props / Parameters#

각 목업은 별도의 통일 컴포넌트 (MockupBrowser / MockupPhone / MockupCode / MockupWindow) 로 제공됩니다. chrome / colour / border-radius override 는 모두 각 컴포넌트의 단일 *Style 슬롯으로 흐릅니다 (Epic #1302).

MockupBrowser#

속성타입기본값설명
addressBarString필수주소창에 표시할 URL 텍스트
child Widget / Component 필수 브라우저 뷰포트 내용
mockupBrowserStyle CoreMockupBrowserStyle? null 브라우저 frame chrome / colour / border-radius override (Epic #1302 nested Style API)

MockupPhone#

속성타입기본값설명
childWidget필수폰 화면 내용
width double? 320 폰 프레임 폭 (logical px / Web px)
mockupPhoneStyle CoreMockupPhoneStyle? null 폰 프레임 colour / border-radius chrome override (Epic #1302 nested Style API)

MockupCode#

속성타입기본값설명
lines List<CoreCodeLine> 필수 표시할 코드 라인 (factory 생성자로 문자열에서 변환 가능)
mockupCodeStyle CoreMockupCodeStyle? null 코드 블록 배경 / 텍스트 colour / prefix colour / border-radius / padding / line spacing / line-number width chrome override (Epic #1302 nested Style API)

스타일 시스템 (Style System)#

Epic #1302 nested Style API. MockupPhoneMockupCode 는 각자 전용 Style 슬롯을 갖습니다. 공유 CoreMockupTheme 의 legacy flat 필드는 백업 fallback 으로 유지됩니다.

CoreMockupPhoneStyle#

슬롯타입비고
backgroundColorCoreColor?폰 화면 배경 colour.
frameColorCoreColor?폰 외부 shell 프레임 fill colour.
borderColor CoreColor? 폰 외부 shell border / outline colour.
borderRadius CoreBorderRadius? 외부 프레임 border radius.

CoreMockupCodeStyle#

슬롯타입비고
backgroundColorCoreColor?코드 블록 배경 colour.
textColorCoreColor?코드 텍스트 colour.
borderRadius CoreBorderRadius? 외부 컨테이너 border radius (logical px).

Resolve chain#

design system default
  → CoreMockupTheme (legacy shared chrome fields)
  → CoreMockupCodeTheme.style / CoreMockupPhoneTheme.style    // 프로젝트 공통
  → parent component slot override
  → widget.mockupCodeStyle / widget.mockupPhoneStyle          // 인스턴스별

MockupWindow#

속성타입기본값설명
child Widget / Component null 윈도우 내용
mockupWindowStyle CoreMockupWindowStyle? null 윈도우 frame chrome / colour / border-radius override (Epic #1302 nested Style API)

변형 (Variants)#

Browser#

주소창이 있는 브라우저 프레임입니다.

MockupBrowser(
  addressBar: 'https://example.com/dashboard',
  child: DashboardScreenshot(),
)

Phone#

모바일 기기 외형(카메라 노치 등)을 갖춘 스마트폰 프레임입니다.

MockupPhone(
  child: MobileAppPreview(),
)

Code#

코드 에디터 스타일의 프레임입니다. 선택적으로 줄 번호가 표시됩니다.

MockupCode.fromString(
  'print("Hello, World!");',
  showLineNumbers: true,
)

Window#

데스크탑 앱 윈도우 스타일의 프레임입니다. 트래픽라이트 컨트롤이 포함됩니다.

MockupWindow(
  child: AppContent(),
)

동작 스펙 (Behavior)#

인터랙션#

  • 기본적으로 정적 프레임으로 인터랙션 없음
  • child에 인터랙션 가능한 위젯을 넣으면 프레임 안에서 동작

상태 전환#

  • 정적 컴포넌트로 별도 상태 전환 없음

애니메이션#

  • 별도 애니메이션 없음 (자식 위젯의 애니메이션은 그대로 동작)

사용 가이드라인 (Usage Guidelines)#

✅ Do#

랜딩 페이지 히어로 섹션에 실제 앱 스크린샷을 Phone 목업으로 표시

MockupPhone(
  child: Image.asset(
    'assets/screenshots/app_home.png',
    fit: BoxFit.cover,
  ),
)

디바이스 프레임 안의 스크린샷은 앱의 실제 UI를 자연스럽게 보여줘 사용자 신뢰를 높인다.


❌ Don't#

실제 동작하는 복잡한 앱을 Phone 목업 안에 렌더링

// ❌ 성능 문제 야기할 수 있음
MockupPhone(
  child: MaterialApp(
    home: ComplexAppWithManyAnimations(),
  ),
)

목업 안에 무거운 위젯을 렌더링하면 성능 문제가 발생한다. 스크린샷 이미지로 대체하는 것이 좋다.

✅ Do#

문서에서 코드 예제를 Code 목업으로 표시해 가독성 향상

MockupCode.fromString(
  "Button(\n  onPressed: handlePressed,\n  child: const Text('클릭'),\n)",
)

코드 에디터 스타일 프레임이 코드 블록을 더 전문적이고 읽기 쉽게 만든다.


❌ Don't#

실제 사용자 인터페이스에 Mockup을 UI 컨테이너로 사용

// ❌ 실제 앱 UI에 Phone 목업 사용
Scaffold(
  body: MockupPhone( // 실제 앱인데 Phone 프레임?
    child: actualAppContent,
  ),
)

Mockup은 프레젠테이션/문서 목적이며 실제 앱 UI 컨테이너로 사용하면 혼란스럽고 불필요한 비주얼 레이어를 추가한다.

✅ Do#

목업에는 실제와 유사한 예시 콘텐츠를 사용하세요.

MockupPhone(
  child: AppScreenPreview(
    // 실제 앱 화면과 유사한 내용
    content: RealisticDemoContent(),
  ),
)

실제와 유사한 콘텐츠로 목업을 채우면 디자인 리뷰나 문서에서 컴포넌트의 실제 사용 모습을 효과적으로 전달할 수 있습니다.


❌ Don't#

목업을 실제 프로덕션 레이아웃의 컨테이너로 사용하지 마세요.

// ❌ 실제 앱에서 Mockup을 레이아웃으로 사용
Scaffold(
  body: MockupBrowser(
    addressBar: 'https://example.com',
    child: actualAppContent,  // 실제 콘텐츠
  ),
)

Mockup은 문서화, 프레젠테이션, Widgetbook 등 데모 목적으로만 사용합니다. 실제 앱 레이아웃으로는 사용하지 마세요.

접근성 (Accessibility)#

키보드 인터랙션#

동작
해당 없음정적 프레임 컴포넌트; 내부 자식 위젯의 키보드 인터랙션은 그대로 동작

스크린 리더#

  • Flutter: Semantics(label: '브라우저 목업: CoUI 문서') 형태로 프레임 설명 추가 권장
  • Web: role="img" + aria-label로 목업 프레임을 장식적 요소로 표시; 내부 콘텐츠 접근성은 자식 위젯이 처리

터치 타겟#

  • 목업 프레임 자체는 터치 타겟 없음
  • 내부 인터랙션 요소는 자식 위젯의 타겟 크기를 따름

크로스 플랫폼 차이점 (Platform Differences)#

항목FlutterWeb
클래스명 MockupBrowser / MockupPhone MockupBrowser / MockupPhone
렌더링CustomPaint 기반 프레임CSS box-shadow + 보더 기반
폰 노치ClipRRect + 노치 페인터CSS pseudo 요소
브라우저 주소창커스텀 위젯HTML + CSS
  • Card: 단순한 콘텐츠 컨테이너가 필요할 때 사용
  • CodeSnippet: 코드 하이라이팅이 필요한 코드 블록에 사용

조합 예제#

// 랜딩 페이지 히어로 섹션
Row(
  children: [
    Expanded(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('CoUI로 아름다운 UI 만들기',
            style: Theme.of(context).textTheme.headlineLarge),
          const Gap.md(),
          Text('Flutter와 Web을 동시에 지원하는 디자인 시스템'),
          const Gap.lg(),
          Button(
            onPressed: handleGetStarted,
            child: const Text('시작하기'),
          ),
        ],
      ),
    ),
    const Gap.xl(),
    Expanded(
      child: MockupBrowser(
        addressBar: 'https://coui.cocode.im',
        child: Image.asset('assets/screenshots/landing.png'),
      ),
    ),
  ],
)