LogoSkills

coui-mockup-window

OS 창 목업, 데스크톱 앱 프레임 미리보기, 신호등 점이 있는 macOS 스타일 창 크롬을 CoUI Flutter(coui_flutter) 또는 CoUI Web(coui_web)에서 MockupWindow 위젯과 CoreMockupWindowStyle, CoreMockupWindowTheme, 공유되는 CoreMockupTheme로 만들 때 활성화합니다.

CoUI MockupWindow#

Quick Reference#

  • Widget: MockupWindow
  • Variant: 없음 (variant 파라미터 없음) — "no semantic enums (no variant, no size)"
  • Size: 없음 (size 파라미터 없음)
  • Style slot: mockupWindowStyle: CoreMockupWindowStyle(...) — the single per-instance style slot covering every chrome/colour/dimensional knob
  • Canonical snippet:
MockupWindow(
  child: const Padding(
    padding: EdgeInsets.all(16),
    child: Text('Window content'),
  ),
)

한마디로#

MockupWindow는 화면 안에 "맥(macOS) 컴퓨터 창 모양의 액자"를 그려주는 부품입니다. 왼쪽 위에 빨강·노랑·초록 점 3개(신호등 버튼)가 달린 창 테두리를 만들고, 그 안에 원하는 내용(스크린샷, 데모 화면, 설명 등)을 넣어 보여줍니다. 진짜 창이 아니라 "창처럼 보이게 하는 장식 프레임"이라서, 점들을 눌러도 아무 일도 일어나지 않습니다. 랜딩 페이지에서 제품 화면을 그럴듯하게 연출할 때 쓰는 사진 액자라고 생각하면 됩니다.

무엇을·언제#

  • 무엇을 해주나요: 내용물 위에 "제목 표시줄 + 신호등 점 3개"가 붙은 데스크톱 창 프레임을 씌워 줍니다. 테두리·둥근 모서리·배경색까지 알아서 통일된 모양으로 나옵니다.
  • 언제 쓰이나요: 랜딩/소개 페이지에서 앱 화면을 창 안에 담아 보여줄 때, 문서·포트폴리오에서 데모 화면을 연출할 때, 디자인 시안에서 "이건 데스크톱 앱 화면이다"라고 표현하고 싶을 때 사용됩니다.
  • 눌리는 건가요: 아니요. 빨강·노랑·초록 점은 순수한 장식이라 클릭/탭해도 동작하지 않습니다.
  • 꾸밀 수 있나요: 배경색, 테두리색, 모서리 둥글기, 제목 표시줄 높이, 점 크기·색까지 세부 설정 한 곳(mockupWindowStyle)에서 바꿀 수 있습니다.
  • 한 번 정해두면 통일됩니다: 앱(Flutter)과 웹 양쪽에서 같은 방식으로 만들어지고, 프로젝트 전체의 기본 모양을 한곳(테마)에서 정해둘 수도 있습니다.
  • 형제 부품도 있습니다: 브라우저 창(MockupBrowser), 휴대폰 프레임(MockupPhone), 코드 화면(MockupCode)도 같은 계열의 "연출용 액자"입니다.

핵심 용어#

용어쉬운 설명
MockupWindow맥 스타일 창 모양을 흉내 내는 장식용 액자 부품
title bar (제목 표시줄)창 맨 위의 가로 띠 — 신호등 점 3개가 들어가는 자리
traffic light (신호등 점)왼쪽 위 빨강(닫기)·노랑(최소화)·초록(최대화) 장식 점 3개
child창 안에 넣는 내용물 (스크린샷, 텍스트, 데모 화면 등)
mockupWindowStyle이 창 하나만 다르게(색·크기·모서리 등) 꾸미는 세부 설정 칸
theme / theming프로젝트 전체 창 액자에 공통 적용되는 기본 모양 설정
Flutter모바일/앱 화면을 만드는 기술
Web웹 브라우저용 화면을 만드는 기술
Legacy / Deprecated옛날 방식이라 새 코드에서는 쓰지 말아야 하는, 곧 사라질 항목

Overview#

MockupWindow is the unified cross-platform OS-window mockup: an outer frame with a title bar carrying three macOS-style traffic-light dots (close/minimize/maximize) above a content slot. It is a pure display component — the dots are decorative, there are no callbacks, and there are no semantic enums (no variant, no size). The entire surface is:

  • child — optional window content (Widget? on Flutter, Component? on Web).
  • mockupWindowStyle — the single per-instance style slot (CoreMockupWindowStyle), covering every chrome/colour/dimensional knob.

It implements the shared CoreMockupWindowContract<W> from coui_core, so Flutter and Web take the same style type and resolve through the same chain. Sibling mockups in the same family: MockupBrowser, MockupPhone, MockupCode — all recolourable at once via the shared CoreMockupTheme.

Rendered structure (both platforms):

frame (background, border, borderRadius, clipped)
  ├─ title bar (height, horizontal padding, bottom border)
  │    ├─ dot: close    (red   #FF5F57)
  │    ├─ dot: minimize (amber #FFBD2E)
  │    └─ dot: maximize (green #28C840)
  └─ content (child)

Flutter (coui_flutter)#

Import#

import 'package:coui_flutter/coui_flutter.dart';

Single barrel only — never import src/ paths.

Basic Usage#

MockupWindow(
  child: const Padding(
    padding: EdgeInsets.all(16),
    child: Text('Window content'),
  ),
)

child is optional — omit it to render just the empty frame with its title bar:

const MockupWindow()

The frame clips its content (clipBehavior: antiAlias), so images or colored children are trimmed to the rounded corners automatically. The column is mainAxisSize: min / crossAxisAlignment: stretch — the window hugs its content height and stretches content to the frame width.

Screenshot / demo frame pattern#

MockupWindow(
  child: SizedBox(
    height: 300,
    child: Center(child: Text('App preview')),
  ),
)

Per-instance Style (mockupWindowStyle)#

mockupWindowStyle takes a CoreMockupWindowStyle. All fields are optional; anything unset falls back through the theme chain to the design-system defaults.

MockupWindow(
  mockupWindowStyle: const CoreMockupWindowStyle(
    borderRadius: CoreBorderRadius.all(CoreRadius.radius8),
    titleBarHeight: 32,
    trafficLightSize: 10,
  ),
  child: const Text('Compact window'),
)

Recolour example (token-bound frame + custom dots):

MockupWindow(
  mockupWindowStyle: const CoreMockupWindowStyle(
    backgroundColor: CoreColor.token(CoreColors.surface),
    borderColor: CoreColor.token(CoreColors.outline),
    trafficLightCloseColor: CoreColor(0xFF888888),
    trafficLightMinimizeColor: CoreColor(0xFF888888),
    trafficLightMaximizeColor: CoreColor(0xFF888888),
  ),
  child: const Text('Monochrome chrome'),
)

Flutter-specific behavior notes#

  • All dimensional values (radius, title-bar spacing/padding/height, dot size/radius) are pre-multiplied by theme.scaling in the resolver — the widget paints them as-is.
  • Colours resolve through theme.colorScheme to paint-ready Colors; the title bar's bottom divider uses the same resolved borderColor as the outer frame.
  • Dot spacing flows into Row.spacing (titleBarSpacing is a scalar, logical px).

Web (coui_web / Jaspr)#

Import#

import 'package:coui_web/coui_web.dart';

Basic Usage#

child is a Jaspr Component:

MockupWindow(
  child: div([text('Window content')], classes: 'p-4'),
)

Multiple content blocks (children)#

Web additionally accepts a children: List<Component>? — rendered before child inside the frame:

MockupWindow(
  children: [
    div([text('Toolbar')], classes: 'p-2'),
    div([text('Body')], classes: 'p-4'),
  ],
)

Per-instance Style (same CoreMockupWindowStyle)#

MockupWindow(
  mockupWindowStyle: const CoreMockupWindowStyle(
    borderRadius: CoreBorderRadius.all(CoreRadius.radius8),
  ),
  child: div([text('Custom radius')]),
)

Colour channels are class-XOR-inline: a token colour (default or token override, e.g. the surface background / outline border) emits a Tailwind bg-*/border-* class and stays dark-mode reactive; a raw CoreColor(0xFF...) override goes inline. The traffic-light defaults are raw macOS palette values, so they render as inline background-color. All dimensional values (radius, gap, padding, height, dot size) are always inline rem — never utility classes.

Web-only constructor extras#

The Web constructor also accepts tag (root element tag, default 'div'), id, classes, css (Jaspr Styles), attributes, and eventHandlers. User classes are merged after the resolved class string; user css is combined after the resolved inline styles. The component also provides copyWith(...).

MockupWindow(
  classes: 'max-w-3xl mx-auto',
  child: div([text('Hero demo')]),
)

Note: the Web root carries w-full — it fills its container's width (constrain it with a wrapper or classes as above), whereas the Flutter frame just follows its parent constraints.

Parameters#

ParameterType (Flutter)Type (Web)DefaultNotes
child Widget? Component? null Window content (optional — empty frame is valid)
mockupWindowStyle CoreMockupWindowStyle? CoreMockupWindowStyle? null Single per-instance chrome/colour/dimension slot

Web-only: children (List<Component>?, rendered before child), tag (default 'div'), id, classes, css (Jaspr Styles), attributes, eventHandlers, plus copyWith(...).

There are no variant / size enums on this component — MockupWindow is a single-form display frame; every visual knob lives on the style slot below.

Style slot — CoreMockupWindowStyle (full field set + defaults)#

Constructor fields (all optional) and their design-system defaults (statics on CoreMockupWindowStyle, shared by Flutter and Web):

FieldTypeDefault (static)Value
backgroundColor CoreColor? defaultBackgroundColor token surface
borderColor CoreColor? defaultBorderColor token outline (frame + title-bar divider)
borderRadius CoreBorderRadius? defaultBorderRadius CoreBorderRadius.all(CoreRadius.radius16)
titleBarSpacing double? defaultTitleBarSpacing CoreSpace.space8 (gap between dots, logical px)
titleBarPadding CoreEdgeInsets? defaultTitleBarPadding CoreEdgeInsets.symmetric(horizontal: CoreSpace.space12)
titleBarHeight double? defaultTitleBarHeight CoreSpace.space40 (logical px)
trafficLightSize double? defaultTrafficLightSize CoreSpace.space12 (logical px)
trafficLightBorderRadius CoreBorderRadius? defaultTrafficLightBorderRadius CoreBorderRadius.all(CoreRadius.radius9999) (perfect circle)
trafficLightCloseColor CoreColor? defaultTrafficLightCloseColor raw CoreColor(0xFFFF5F57) (macOS red)
trafficLightMinimizeColor CoreColor? defaultTrafficLightMinimizeColor raw CoreColor(0xFFFFBD2E) (macOS yellow)
trafficLightMaximizeColor CoreColor? defaultTrafficLightMaximizeColor raw CoreColor(0xFF28C840) (macOS green)

CoreMockupWindowStyle also exposes merge(other) and copyWith(...).

Project-level theming#

Two theme slots participate, both { style }-shaped:

  • CoreMockupWindowTheme (coreComponentTheme.mockupWindow) — window-specific project defaults, carries a full CoreMockupWindowStyle.
  • CoreMockupTheme (coreComponentTheme.mockup) — shared across all mockup variants (Browser/Code/Phone/Window). Its CoreMockupStyle exposes only backgroundColor / borderColor / frameColor / borderRadius; the Window resolver consumes the first three minus frameColor (phone-only) — i.e. background, border, and radius can be recoloured for every mockup frame at once.

Resolve chain (lowest → highest priority; identical on both platforms):

CoreMockupWindowStyle design-system defaults
  → CoreMockupTheme.style          // shared cross-variant (bg/border/radius only)CoreMockupWindowTheme.style    // 프로젝트 공통
  → widget.mockupWindowStyle       // 인스턴스별
const CoreMockupWindowTheme(
  style: CoreMockupWindowStyle(
    borderRadius: CoreBorderRadius.all(CoreRadius.radius8),
    titleBarHeight: 32,
  ),
)

Register it on the app theme's coreComponentTheme.mockupWindow slot; the shared recolour goes on coreComponentTheme.mockup (CoreMockupTheme(style: CoreMockupStyle(...))). Both platform resolvers read the same two slots.

Flutter ↔ Web Differences#

AspectFlutterWeb
Widget class MockupWindow (StatelessWidget, CoreMockupWindowContract<Widget>) MockupWindow (UiComponent, CoreMockupWindowContract<Component>)
Child type Widget? Component? + extra children: List<Component>?
Content order title bar → child title bar → ...childrenchild
Render Container + Column, clipBehavior: antiAlias element (default <div> , configurable tag ) with Tailwind className + inline styles, overflow-hidden relative
Width behavior follows parent constraints root gets w-full (fills container)
Dimensions pre-multiplied by theme.scaling, painted as logical px emitted as inline rem CSS
Colours resolved via theme.colorScheme to paint-ready Color token → Tailwind class (dark-mode reactive); raw ARGB → inline CSS
Extra ctor params tag , id , classes , css , attributes , eventHandlers ; also copyWith(...)

Shared on both: mockupWindowStyle: CoreMockupWindowStyle, CoreMockupWindowTheme / shared CoreMockupTheme resolve chain, decorative-only traffic lights (no callbacks anywhere).

Pitfalls#

  • The traffic-light dots (close/minimize/maximize) are purely decorative — "the dots are decorative, there are no callbacks" — clicking/tapping them does nothing.
  • On Web the root element carries w-full and fills its container by default, unlike Flutter which just follows parent constraints — constrain it with a wrapper or your own classes if you don't want full width.
  • The shared CoreMockupTheme's CoreMockupStyle also exposes a frameColor field, but the Window resolver only consumes backgroundColor/borderColor/borderRadius from it — frameColor is phone-only and has no effect on MockupWindow.
  • v0.127 is a de-prefix hard break: the old Co-prefixed class (CoMockupWindow) has no alias — it was removed outright, not just deprecated, while app-shell exceptions like CoUIApp, CoUILocalizations, CoUIScrollBehavior keep their prefix.
  • On Web, colour channels are class-XOR-inline: token colours (defaults/overrides) emit Tailwind bg-*/border-* classes and stay dark-mode reactive, but a raw CoreColor(0xFF...) override (e.g. custom traffic-light colours) renders as inline CSS instead.

v0.127 Migration (Legacy)#

v0.127 is a de-prefix hard break: the widget class was renamed and no Co-prefixed alias remains. Core* contract names (CoreMockupWindowStyle, CoreMockupWindowTheme, CoreMockupTheme, CoreMockupWindowContract) are unchanged.

❌ Legacy (pre-0.127, removed)✅ v0.127
CoMockupWindow(child: …)MockupWindow(child: …)
CoMockupBrowser(…) (sibling)MockupBrowser(…)
CoMockupPhone(…) (sibling)MockupPhone(…)
CoMockupCode(…) (sibling)MockupCode(…)

Additional migration notes:

  • Migration is rename-only for this component: ❌ CoMockupWindow(...)MockupWindow(...) with identical parameters (child, mockupWindowStyle).
  • App-shell exceptions that keep the prefix: CoUIApp, CoUILocalizations, CoUIScrollBehavior.
  • Import stays the single barrel: package:coui_flutter/coui_flutter.dart / package:coui_web/coui_web.dart — never src/ paths.