Craft: Native-First Dokument-Exzellenz
Warum Craft den Apple Design Award 2021 gewann: Native Performance, verschachtelte Seiten, Block-Architektur und Ein-Klick-Web-Publishing. Mit Swift-Implementierungsmustern.
Craft: Native-First-Dokumentenexzellenz
„Wir glauben, dass Werkzeuge für das Denken sich wie eine Erweiterung der eigenen Gedanken anfühlen sollten, nicht wie ein Hindernis."
Craft beweist, dass native Apps Erlebnisse bieten können, die Web-Apps nicht erreichen. Durch tiefe Plattformintegration erreicht es eine Reaktionsfähigkeit und Qualität, die digitales Notizenmachen so natürlich wie Papier erscheinen lässt.
Wichtigste Erkenntnisse
- Native schlägt Electron - Reaktionszeiten unter 50ms erfordern plattformspezifische UI, keine Web-Wrapper
- Seiten in Seiten - Jeder Block kann zur Seite werden, sodass Struktur aus dem Inhalt entsteht
- Mehrere Layouts, dieselben Daten - Listen, Karten, Galerien und Boards bieten die richtige Ansicht für jeden Kontext
- Web-Publishing mit einem Klick - Geteilte Seiten funktionieren ohne Empfänger-Accounts oder App-Downloads
- Plattformgerecht, nicht identisch - Mac Craft fühlt sich nach Mac an; iOS Craft fühlt sich nach iOS an
Warum Craft wichtig ist
Craft gewann den Apple Design Award 2021 und erhält konstant App Store-Auszeichnungen, weil es sich weigert, native Performance für plattformübergreifende Bequemlichkeit zu opfern.
Wichtigste Errungenschaften: - Echte native Apps auf iOS, macOS, Windows (nicht Electron) - Reaktionszeit unter 50ms bei allen Interaktionen - Offline-first mit nahtloser Synchronisierung - Block-basierte Architektur ohne die Web-App-Verzögerungen - Schöne geteilte Seiten, die ohne Accounts funktionieren
Kernphilosophie des Designs
Native-First, nicht Web-verpackt
Crafts entscheidende Wahl: native Apps für jede Plattform entwickeln, mit geteilter Geschäftslogik aber plattformspezifischer UI.
ELECTRON/WEB APPROACH CRAFT'S NATIVE APPROACH
───────────────────────────────────────────────────────────────────
Single codebase (JavaScript) Platform-specific UI (Swift, etc.)
Web rendering engine Native rendering
Cross-platform "consistency" Platform-appropriate behavior
~200ms input latency ~16ms input latency
Generic keyboard shortcuts Platform-native shortcuts
Web-standard text selection Native text engine
Zentrale Erkenntnis: Benutzer wollen keine Apps, die überall gleich aussehen – sie wollen Apps, die sich auf ihrer Plattform richtig anfühlen.
macOS via Catalyst (richtig gemacht)
Craft nutzt Mac Catalyst, passt es aber umfassend an, damit es sich wirklich Mac-nativ anfühlt:
// Craft's Catalyst customization approach
#if targetEnvironment(macCatalyst)
extension SceneDelegate {
func scene(_ scene: UIScene, willConnectTo session: UISceneSession) {
guard let windowScene = scene as? UIWindowScene else { return }
// Enable native Mac toolbar
if let titlebar = windowScene.titlebar {
titlebar.titleVisibility = .hidden
titlebar.toolbar = createNativeToolbar()
}
// Mac-specific window sizing
windowScene.sizeRestrictions?.minimumSize = CGSize(width: 800, height: 600)
// Enable window tabs
UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.forEach { $0.titlebar?.toolbar?.showsBaselineSeparator = false }
}
private func createNativeToolbar() -> NSToolbar {
let toolbar = NSToolbar(identifier: "CraftToolbar")
toolbar.displayMode = .iconOnly
toolbar.delegate = self
return toolbar
}
}
#endif
Musterbibliothek
1. Block-basierte Inhaltsarchitektur
Jedes Element in Craft ist ein Block. Jeder Block mit Inhalt ist potenziell eine Seite, was unendliche Verschachtelung ohne kognitive Überlastung ermöglicht.
Block-Typen:
TEXT BLOCKS
├── Paragraph (default)
├── Heading 1, 2, 3
├── Quote
├── Callout (info, warning, success)
└── Code (with syntax highlighting)
MEDIA BLOCKS
├── Image (with caption)
├── Video
├── File attachment
├── Drawing (Apple Pencil)
└── Audio recording
STRUCTURAL BLOCKS
├── Toggle (collapsible)
├── Page (nested document)
├── Divider
└── Table
Implementierungskonzept:
protocol CraftBlock: Identifiable {
var id: UUID { get }
var content: BlockContent { get set }
var style: BlockStyle { get set }
var children: [any CraftBlock] { get set }
var metadata: BlockMetadata { get }
}
enum BlockContent {
case text(AttributedString)
case image(ImageData)
case page(PageReference)
case toggle(isExpanded: Bool)
case code(language: String, content: String)
case table(TableData)
}
struct BlockMetadata {
let created: Date
let modified: Date
let createdBy: UserReference?
}
// Every block can contain other blocks
class PageBlock: CraftBlock {
var id = UUID()
var content: BlockContent
var style: BlockStyle
var children: [any CraftBlock] = []
var metadata: BlockMetadata
// A page is just a block that can be opened full-screen
var canOpenAsPage: Bool { true }
}
Zentrale Erkenntnis: Wenn jeder Block eine Seite sein kann, entsteht die Informationsarchitektur aus dem Inhalt, nicht aus vorgegebener Struktur.
2. Verschachtelte Seiten (Seiten in Seiten)
Crafts Alleinstellungsmerkmal: Jeder Block kann zur Seite werden, und Seiten verschachteln sich unendlich.
DOCUMENT STRUCTURE
───────────────────────────────────────────────────────────────────
📄 Project Alpha
├── 📝 Introduction paragraph
├── 📄 Research Notes ← This is a page (nested document)
│ ├── 📝 User interviews
│ ├── 📄 Interview: Sarah ← Another nested page
│ │ └── 📝 Key insights
│ └── 📝 Competitive analysis
├── 📝 Timeline overview
└── 📄 Meeting Notes ← Another page
└── 📝 Action items
Navigation: Breadcrumb trail shows path
Project Alpha > Research Notes > Interview: Sarah
SwiftUI-Implementierungsmuster:
struct PageView: View {
@Bindable var page: PageDocument
@State private var selectedBlock: CraftBlock?
var body: some View {
ScrollView {
LazyVStack(alignment: .leading, spacing: 0) {
ForEach(page.blocks) { block in
BlockView(block: block, onTap: { tapped in
if tapped.canOpenAsPage {
navigateToPage(tapped)
} else {
selectedBlock = tapped
}
})
}
}
}
.navigationTitle(page.title)
.toolbar {
// Breadcrumb navigation
ToolbarItem(placement: .principal) {
BreadcrumbView(path: page.ancestorPath)
}
}
}
}
struct BreadcrumbView: View {
let path: [PageReference]
var body: some View {
HStack(spacing: 4) {
ForEach(Array(path.enumerated()), id: \.element.id) { index, page in
if index > 0 {
Image(systemName: "chevron.right")
.font(.caption)
.foregroundStyle(.secondary)
}
Button(page.title) {
navigateTo(page)
}
.buttonStyle(.plain)
.foregroundStyle(index == path.count - 1 ? .primary : .secondary)
}
}
}
}
3. Karten-Layout-System
Craft bietet 5 visuelle Stile für Seiten, die Dokumente auf einen Blick überschaubar machen.
Kartenstile:
STIL 1: LISTE (Standard)
┌────────────────────────────────────────┐
│ 📄 Seitentitel │
│ Vorschautext erscheint hier... │
└────────────────────────────────────────┘
STIL 2: KARTE (Mittel)
┌──────────────────┐
│ ┌──────────────┐ │
│ │ [Bild] │ │
│ └──────────────┘ │
│ Seitentitel │
│ Vorschautext... │
└──────────────────┘
STIL 3: KARTE (Groß)
┌────────────────────────────────────────┐
│ ┌────────────────────────────────────┐ │
│ │ │ │
│ │ [Titelbild] │ │
│ │ │ │
│ └────────────────────────────────────┘ │
│ Seitentitel │
│ Längerer Vorschautext mit mehr Platz...│
└────────────────────────────────────────┘
STIL 4: GALERIE (Raster)
┌────────┐ ┌────────┐ ┌────────┐
│ [Bild] │ │ [Bild] │ │ [Bild] │
│ Titel │ │ Titel │ │ Titel │
└────────┘ └────────┘ └────────┘
STIL 5: BOARD (Kanban-Stil)
│ To Do │ In Arbeit│ Erledigt │
├──────────┼──────────┼──────────┤
│ Aufgabe 1│ Aufgabe 3│ Aufgabe 5│
│ Aufgabe 2│ Aufgabe 4│ │
Implementierung:
enum PageLayoutStyle: String, CaseIterable {
case list = "list"
case cardMedium = "card_medium"
case cardLarge = "card_large"
case gallery = "gallery"
case board = "board"
}
struct PageGridView: View {
let pages: [PageReference]
let style: PageLayoutStyle
var body: some View {
switch style {
case .list:
LazyVStack(spacing: 8) {
ForEach(pages) { page in
ListRowView(page: page)
}
}
case .cardMedium, .cardLarge:
let columns = style == .cardMedium ? 3 : 2
LazyVGrid(columns: Array(repeating: .init(.flexible()), count: columns)) {
ForEach(pages) { page in
CardView(page: page, size: style == .cardLarge ? .large : .medium)
}
}
case .gallery:
LazyVGrid(columns: Array(repeating: .init(.flexible()), count: 4)) {
ForEach(pages) { page in
GalleryThumbnail(page: page)
}
}
case .board:
BoardView(pages: pages)
}
}
}
4. Seitenhintergründe und Theming
Jede Seite kann durch Hintergründe und Akzentfarben ihre eigene visuelle Identität erhalten.
struct PageAppearance {
// Background options
enum Background {
case solid(Color)
case gradient(Gradient)
case image(ImageReference)
case paper(PaperTexture)
}
// Paper textures for writing feel
enum PaperTexture: String {
case none
case lined
case grid
case dotted
}
var background: Background = .solid(.white)
var accentColor: Color = .blue
var iconEmoji: String?
var coverImage: ImageReference?
}
// Applied to page
struct PageContainer: View {
let page: PageDocument
var body: some View {
ZStack {
// Background layer
backgroundView(for: page.appearance.background)
// Content layer
PageContentView(page: page)
}
}
@ViewBuilder
private func backgroundView(for background: PageAppearance.Background) -> some View {
switch background {
case .solid(let color):
color.ignoresSafeArea()
case .gradient(let gradient):
LinearGradient(gradient: gradient, startPoint: .top, endPoint: .bottom)
.ignoresSafeArea()
case .image(let ref):
AsyncImage(url: ref.url) { image in
image.resizable().scaledToFill()
} placeholder: {
Color.gray.opacity(0.1)
}
.ignoresSafeArea()
.overlay(.ultraThinMaterial)
case .paper(let texture):
PaperTextureView(texture: texture)
}
}
}
5. Share Pages (Web-Veröffentlichung)
Craft generiert aus Dokumenten ansprechende, responsive Webseiten. Zum Ansehen ist kein Konto erforderlich.
DOKUMENT IN CRAFT SHARE PAGE IM WEB
───────────────────────────────────────────────────────────────────
📄 Projektvorschlag https://www.craft.do/s/abc123
├── 📝 Zusammenfassung →
├── 📄 Budgetdetails Sauberes, responsives Layout
├── 📝 Zeitplan Typografie erhalten
└── 📄 Team-Biografien Bilder optimiert
Dark Mode unterstützt
Kein Craft-Konto erforderlich
Wichtige Funktionen: - Veröffentlichung mit einem Klick - Eigene Domains verfügbar - Passwortschutz optional - Aufrufstatistiken - SEO-freundliche Darstellung - Responsive auf allen Geräten
Visuelles Design-System
Farbpalette
extension Color {
// Craft's signature palette
static let craftPurple = Color(hex: "#6366F1") // Primary accent
static let craftBackground = Color(hex: "#FAFAFA") // Light mode
static let craftSurface = Color(hex: "#FFFFFF")
// Semantic colors
static let craftSuccess = Color(hex: "#10B981")
static let craftWarning = Color(hex: "#F59E0B")
static let craftError = Color(hex: "#EF4444")
static let craftInfo = Color(hex: "#3B82F6")
// Text hierarchy
static let craftTextPrimary = Color(hex: "#111827")
static let craftTextSecondary = Color(hex: "#6B7280")
static let craftTextMuted = Color(hex: "#9CA3AF")
}
Typografie
struct CraftTypography {
// Document typography
static let title = Font.system(size: 32, weight: .bold, design: .default)
static let heading1 = Font.system(size: 28, weight: .semibold)
static let heading2 = Font.system(size: 22, weight: .semibold)
static let heading3 = Font.system(size: 18, weight: .medium)
static let body = Font.system(size: 16, weight: .regular)
static let caption = Font.system(size: 14, weight: .regular)
// Code blocks
static let code = Font.system(size: 14, weight: .regular, design: .monospaced)
// Line heights (generous for readability)
static let bodyLineSpacing: CGFloat = 8
static let headingLineSpacing: CGFloat = 4
}
Abstands-System
struct CraftSpacing {
// Block spacing
static let blockGap: CGFloat = 4 // Between blocks
static let sectionGap: CGFloat = 24 // Between sections
static let pageMargin: CGFloat = 40 // Page edges (desktop)
static let mobileMargin: CGFloat = 16 // Page edges (mobile)
// Content width
static let maxContentWidth: CGFloat = 720 // Optimal reading
static let wideContentWidth: CGFloat = 960 // Tables, galleries
}
Animations-Philosophie
Flüssige Seitenübergänge
// Navigation between pages uses matched geometry
struct NavigationTransition: View {
@Namespace private var namespace
@State private var selectedPage: PageReference?
var body: some View {
ZStack {
// Page list
PageListView(
onSelect: { page in
withAnimation(.spring(response: 0.35, dampingFraction: 0.85)) {
selectedPage = page
}
},
namespace: namespace
)
// Selected page expands from its card position
if let page = selectedPage {
PageDetailView(page: page)
.matchedGeometryEffect(id: page.id, in: namespace)
.transition(.scale.combined(with: .opacity))
}
}
}
}
Block-Animationen
// Blocks animate when reordering
struct AnimatedBlockList: View {
@Binding var blocks: [CraftBlock]
var body: some View {
LazyVStack(spacing: CraftSpacing.blockGap) {
ForEach(blocks) { block in
BlockView(block: block)
.transition(.asymmetric(
insertion: .move(edge: .top).combined(with: .opacity),
removal: .move(edge: .bottom).combined(with: .opacity)
))
}
}
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: blocks)
}
}
Erkenntnisse für unsere Arbeit
1. Native Performance ist die Investition wert
Nutzer spüren den Unterschied zwischen 16ms und 200ms Latenz. Native Apps gewinnen beim Gefühl.
2. Seiten innerhalb von Seiten ermöglichen organische Organisation
Lass Struktur aus dem Inhalt entstehen. Zwinge Nutzer nicht, Hierarchien von Anfang an festzulegen.
3. Mehrere visuelle Layouts für denselben Inhalt
Karten, Listen, Galerien—dieselben Daten, unterschiedliche Ansichten für verschiedene Kontexte.
4. Teilen ohne Reibungsverluste
One-Click Web-Publishing beseitigt das „Wie teile ich das?"-Problem vollständig.
5. Plattformgerecht, nicht plattformidentisch
Craft auf dem Mac fühlt sich an wie eine Mac-App. Craft auf iOS fühlt sich an wie eine iOS-App. Das ist das Ziel.
Häufig gestellte Fragen
Wie unterscheidet sich Craft von Notion?
Craft ist nativ (speziell für Apple-Plattformen entwickelt), während Notion web-basiert ist. Das bedeutet, Craft erreicht Reaktionszeiten unter 50ms, funktioniert vollständig offline und integriert sich tief in iOS/macOS-Funktionen wie Apple Pencil, Shortcuts und systemweite Suche. Notion bietet mehr Datenbankfunktionen; Craft priorisiert das Schreiberlebnis.
Kann ich Craft offline nutzen?
Ja. Craft speichert alle Dokumente lokal und synchronisiert über iCloud, wenn eine Verbindung besteht. Du kannst Dokumente erstellen, bearbeiten und organisieren, ohne Internet. Änderungen synchronisieren sich automatisch, wenn du dich wieder verbindest.
Was passiert, wenn ich eine Craft-Seite teile?
Craft generiert eine responsive Webseite unter einer craft.do-URL. Empfänger können sie in jedem Browser ansehen, ohne ein Konto zu erstellen oder die App zu installieren. Die Seite bewahrt die Typografie, Bilder und verschachtelte Seitenstruktur deines Dokuments.
Unterstützt Craft Markdown?
Craft verwendet sein eigenes Block-Format, exportiert aber nach Markdown. Du kannst Inhalte als Markdown kopieren oder ganze Dokumente exportieren. Einige Markdown-Shortcuts funktionieren beim Bearbeiten (wie # für Überschriften), aber Craft betont visuelles Bearbeiten gegenüber Plain-Text-Markup.
Wie funktionieren verschachtelte Seiten in Craft?
Jeder Textblock kann zur Seite werden, indem man das Seiten-Icon drückt oder /page tippt. Verschachtelte Seiten erscheinen inline im übergeordneten Dokument und können im Vollbildmodus geöffnet werden. Die Breadcrumb-Navigation zeigt deine Position in der Hierarchie. Das schafft natürliche Organisation, ohne von Anfang an Ordnerstrukturen zu erzwingen.