design/arc

4 min czytania 863 słów
design/arc screenshot

Arc Browser: Nowe spojrzenie na interfejs przeglądarki

“Przeglądarka to teraz system operacyjny. Dlaczego więc nadal wygląda jak oprogramowanie z 2008 roku?” — Josh Miller, The Browser Company

Arc zakwestionował każde założenie dotyczące przeglądarek, od rozmieszczenia kart po zarządzanie oknami. Rezultatem jest przeglądarka, która bardziej przypomina narzędzie kreatywne niż aplikację użytkową.


Najważniejsze wnioski

  1. Pionowy pasek boczny przewyższa poziome karty - Tytuły kart pozostają czytelne, a przestrzeń pionowa jest obfita na współczesnych szerokoekranowych monitorach
  2. Przestrzenie oddzielają konteksty mentalne - Karty służbowe, osobiste i projektowe nigdy się nie mieszają, co zmniejsza tarcie poznawcze
  3. Command Bar > pasek URL - Uniwersalne wyszukiwanie w kartach, historii i akcjach eliminuje potrzebę idealnej organizacji
  4. Personalizacja przez użytkownika buduje lojalność - Boosts pozwalają użytkownikom samodzielnie naprawiać irytujące strony, tworząc osobiste zaangażowanie
  5. Dopasuj wagę interfejsu do zadania - Little Arc dowodzi, że nie każde zadanie wymaga pełnego interfejsu przeglądarki

Dlaczego Arc ma znaczenie

Arc udowodnił, że nawet najbardziej utrwalone interfejsy można przemyśleć na nowo. Chrome, Firefox, Safari wszystkie stosują ten sam podstawowy układ ustalony dekady temu. Arc uwolnił się, traktując przeglądarkę jako przestrzeń roboczą, a nie menedżer okien.

Kluczowe osiągnięcia: - Całkowita eliminacja tradycyjnego paska kart - Interfejs przeglądarki naturalnie wkomponowany w macOS - Stworzenie znaczącego podziału na prywatne i służbowe - Pionierska personalizacja stron na poziomie przeglądarki - Zaprojektowany z myślą o nawigacji klawiaturą


Podstawowe zasady projektowe

1. Rewolucja paska bocznego

Najbardziej widoczna innowacja Arc: zastąpienie poziomych kart pionowym paskiem bocznym, który organizuje zawartość według kontekstu, a nie chronologii.

TRADITIONAL TAB BAR:
┌──────────────────────────────────────────────────────────────┐
│ [←][→][↻] │ Tab 1 │ Tab 2 │ Tab 3 │ Tab 4 │ Tab 5 │ ... │ + │
├──────────────────────────────────────────────────────────────┤
│                                                              │
│                    Page Content                              │
│                                                              │
└──────────────────────────────────────────────────────────────┘
 Problems: Tabs shrink to unreadable, no organization, endless row

ARC'S SIDEBAR MODEL:
┌────────────────┬─────────────────────────────────────────────┐
│  [*] Space     │                                             │
│  ───────────── │                                             │
│  Pinned        │                                             │
│    Gmail       │                                             │
│    Calendar    │          Page Content                       │
│    Notion      │          (full width)                       │
│                │                                             │
│  ───────────── │                                             │
│  Today         │                                             │
│    Tab 1       │                                             │
│    Tab 2       │                                             │
│    Tab 3       │                                             │
│                │                                             │
│  [+ New Tab]   │                                             │
└────────────────┴─────────────────────────────────────────────┘
 Benefits: Readable titles, pinned favorites, organized by purpose

Kluczowa obserwacja: Przestrzeń pozioma jest cenna; przestrzeń pionowa jest obfita. Użycie paska bocznego pozwala tytułom kart oddychać.

Wzorzec CSS (zwijany pasek boczny):

.sidebar {
  --sidebar-width: 240px;
  --sidebar-collapsed: 48px;

  width: var(--sidebar-width);
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--surface-1);
  transition: width 0.2s ease-out;
}

.sidebar.collapsed {
  width: var(--sidebar-collapsed);
}

.sidebar.collapsed .tab-title {
  opacity: 0;
  pointer-events: none;
}

/* Hover to peek */
.sidebar.collapsed:hover {
  width: var(--sidebar-width);
}

.sidebar.collapsed:hover .tab-title {
  opacity: 1;
  transition: opacity 0.15s ease-in 0.1s; /* Delay for smooth reveal */
}

Wzorzec SwiftUI:

struct SidebarView: View {
    @State private var isCollapsed = false
    @State private var isHovering = false

    private var effectiveWidth: CGFloat {
        isCollapsed && !isHovering ? 48 : 240
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 0) {
            PinnedSection()
            Divider()
            TodaySection()
            Spacer()
            NewTabButton()
        }
        .frame(width: effectiveWidth)
        .animation(.easeOut(duration: 0.2), value: effectiveWidth)
        .onHover { isHovering = $0 }
    }
}

2. Spaces: organizacja oparta na kontekście

Spaces oddzielają pracę od życia prywatnego, projekt od projektu. Każda Space to kompletny kontekst przeglądarki z własnymi kartami, przypiętymi stronami, a nawet motywem.

SPACE ARCHITECTURE:

┌─────────────────────────────────────────────────────────────┐
│                        Arc Browser                          │
├───────────┬───────────┬───────────┬─────────────────────────┤
│  Work     │  Personal │  Project  │                         │
│  Space    │  Space    │  Space    │    Content Area         │
│  ─────    │  ─────    │  ─────    │                         │
│  • Slack  │  • Gmail  │  • GitHub │                         │
│  • Docs   │  • Reddit │  • Figma  │                         │
│  • Jira   │  • Netflix│  • Notion │                         │
└───────────┴───────────┴───────────┴─────────────────────────┘

Each Space has:
├── Unique pinned tabs
├── Separate "Today" tabs
├── Own color theme/gradient
├── Isolated browsing context
└── Keyboard shortcut (Cmd+1, Cmd+2, etc.)

Kluczowa obserwacja: Konteksty mentalne nie powinny się przenikać. Karty służbowe pojawiające się w czasie prywatnym tworzą tarcie poznawcze.

Model danych:

interface Space {
  id: string;
  name: string;
  icon?: string;
  gradient: {
    from: string;
    to: string;
    angle: number;
  };
  pinnedTabs: Tab[];
  todayTabs: Tab[];
  archivedTabs: Tab[];
  profile?: BrowserProfile; // Different cookies, history, etc.
}

interface Tab {
  id: string;
  url: string;
  title: string;
  favicon?: string;
  isPinned: boolean;
  lastAccessed: Date;
  parentFolderId?: string;
}

Implementacja SwiftUI:

struct SpaceSwitcher: View {
    @Binding var currentSpace: Space
    let spaces: [Space]

    var body: some View {
        HStack(spacing: 8) {
            ForEach(spaces) { space in
                SpaceButton(
                    space: space,
                    isActive: space.id == currentSpace.id
                ) {
                    withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
                        currentSpace = space
                    }
                }
            }
        }
        .padding(.horizontal, 12)
    }
}

struct SpaceButton: View {
    let space: Space
    let isActive: Bool
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Circle()
                .fill(
                    LinearGradient(
                        colors: [space.gradient.from, space.gradient.to],
                        startPoint: .topLeading,
                        endPoint: .bottomTrailing
                    )
                )
                .frame(width: 24, height: 24)
                .overlay {
                    if isActive {
                        Circle()
                            .strokeBorder(.white, lineWidth: 2)
                    }
                }
        }
        .buttonStyle(.plain)
        .keyboardShortcut(space.keyboardShortcut)
    }
}

3. Command Bar: Spotlight dla przeglądarki

Command Bar Arc (Cmd+T) to więcej niż pasek URL. Zapewnia uniwersalne wyszukiwanie w kartach, historii, zakładkach i akcjach.

COMMAND BAR INTERACTION:

┌─────────────────────────────────────────────────────────────┐
│  > Search tabs, history, or type a URL...                   │
├─────────────────────────────────────────────────────────────┤
│  OPEN TABS                                                  │
│    [doc] Notion - Project Planning            Cmd+1         │
│    [doc] GitHub - Pull Request #123                         │
│                                                             │
│  HISTORY                                                    │
│    [>] Figma - Design System (2 hours ago)                  │
│    [>] MDN - CSS Grid Guide (yesterday)                     │
│                                                             │
│  ACTIONS                                                    │
│    [*] New Note                               Cmd+Shift+N   │
│    [*] Split View                             Cmd+Shift+\   │
│    [*] Copy URL                               Cmd+Shift+C   │
└─────────────────────────────────────────────────────────────┘

Kluczowa obserwacja: Znajdowanie powinno być szybsze niż organizowanie. Dobre wyszukiwanie eliminuje potrzebę idealnej organizacji.

Wzorzec CSS:

.command-bar {
  --bar-width: min(600px, 90vw);

  position: fixed;
  top: 20%;
  left: 50%;
  transform: translateX(-50%);
  width: var(--bar-width);
  background: var(--surface-elevated);
  border-radius: var(--radius-lg);
  box-shadow:
    0 4px 24px rgba(0, 0, 0, 0.2),
    0 0 0 1px rgba(255, 255, 255, 0.1);
  overflow: hidden;
}

.command-input {
  width: 100%;
  padding: 16px 20px;
  font-size: 18px;
  background: transparent;
  border: none;
  color: var(--text-primary);
}

.command-results {
  max-height: 400px;
  overflow-y: auto;
  border-top: 1px solid var(--border-subtle);
}

.command-result {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 20px;
  cursor: pointer;
}

.command-result:hover,
.command-result.selected {
  background: var(--surface-hover);
}

.command-result-shortcut {
  margin-left: auto;
  font-size: 12px;
  color: var(--text-secondary);
  font-family: var(--font-mono);
}

4. Boosts: personalizacja na poziomie użytkownika

Boosts pozwalają użytkownikom wstrzykiwać własny CSS i JavaScript na dowolną stronę: osobne tryby ciemne, uporządkowane interfejsy lub rozszerzona funkcjonalność.

BOOST CONCEPT:

Without Boost:                    With Boost:
┌─────────────────────┐          ┌─────────────────────┐
│ [Header]            │          │                     │
│ [Navigation]        │          │                     │
│ [Sidebar] [Content] │    →     │     [Content]       │
│ [Footer]            │          │     (clean, focused)│
│ [Cookie Banner]     │          │                     │
└─────────────────────┘          └─────────────────────┘

User-created CSS:
- Hide distracting elements
- Change colors/fonts
- Improve readability

Kluczowa obserwacja: Każdy użytkownik ma strony, które odwiedza codziennie, ale chciałby, żeby działały inaczej. Daj im kontrolę.

Wzorzec implementacji:

interface Boost {
  id: string;
  name: string;
  domain: string; // "github.com" or "*.google.com"
  enabled: boolean;
  css?: string;
  js?: string;
  createdAt: Date;
}

// Example Boost: Clean YouTube
const youtubeBoost: Boost = {
  id: 'youtube-clean',
  name: 'Clean YouTube',
  domain: 'youtube.com',
  enabled: true,
  css: `
    /* Hide recommendations */
    #secondary,
    ytd-browse[page-subtype="home"] ytd-rich-grid-renderer {
      display: none !important;
    }

    /* Hide comments */
    #comments {
      display: none !important;
    }

    /* Expand video */
    #primary {
      max-width: 100% !important;
    }
  `
};

Edytor Boost w SwiftUI:

struct BoostEditor: View {
    @Binding var boost: Boost
    @State private var activeTab: BoostTab = .css

    enum BoostTab: String, CaseIterable {
        case css = "CSS"
        case javascript = "JavaScript"
    }

    var body: some View {
        VStack(spacing: 0) {
            // Domain selector
            HStack {
                Image(systemName: "globe")
                TextField("Domain (e.g., github.com)", text: $boost.domain)
                    .textFieldStyle(.plain)
            }
            .padding()
            .background(.ultraThinMaterial)

            // Tab selector
            Picker("", selection: $activeTab) {
                ForEach(BoostTab.allCases, id: \.self) { tab in
                    Text(tab.rawValue).tag(tab)
                }
            }
            .pickerStyle(.segmented)
            .padding()

            // Code editor
            CodeEditor(
                text: activeTab == .css ? $boost.css : $boost.js,
                language: activeTab == .css ? .css : .javascript
            )
        }
    }
}

5. Little Arc: minimalizm z założenia

Little Arc to osobne, minimalne okno do szybkich zadań: jedna karta, bez interfejsu, bez rozpraszaczy.

FULL ARC:                         LITTLE ARC:
┌────────┬────────────────┐      ┌────────────────────────┐
│Sidebar │                │      │ ← google.com/search... │
│        │    Content     │      ├────────────────────────┤
│        │                │      │                        │
│        │                │  →   │    Search Results      │
│        │                │      │    (just the content)  │
└────────┴────────────────┘      └────────────────────────┘

Use cases:
- Quick search
- Checking one thing
- Opening links from other apps

Kluczowa obserwacja: Nie każde zadanie wymaga pełnej przeglądarki. Dopasuj interfejs do wagi zadania.

Wzorzec CSS (minimalny interfejs):

.mini-browser {
  --chrome-height: 36px;

  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}

.mini-chrome {
  height: var(--chrome-height);
  display: flex;
  align-items: center;
  padding: 0 12px;
  background: var(--surface-1);
  gap: 8px;
}

.mini-url-bar {
  flex: 1;
  padding: 4px 8px;
  font-size: 13px;
  background: var(--surface-2);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
}

.mini-content {
  height: calc(100% - var(--chrome-height));
}

Wzorce do przeniesienia

Wzorzec 1: efemeryczne vs. trwałe

Sekcja „Today" w Arc automatycznie archiwizuje karty po 12 godzinach. Elementy trwałe muszą być jawnie przypięte.

interface EphemeralityConfig {
  defaultLifetime: number; // milliseconds
  onExpire: 'archive' | 'close' | 'prompt';
  exceptions: string[]; // domains that never expire
}

function shouldArchive(tab: Tab, config: EphemeralityConfig): boolean {
  const age = Date.now() - tab.lastAccessed.getTime();
  if (config.exceptions.includes(new URL(tab.url).hostname)) {
    return false;
  }
  return age > config.defaultLifetime;
}

Wzorzec 2: widok podzielony

Przeglądanie obok siebie bez zarządzania oddzielnymi oknami.

struct SplitView: View {
    @State private var splitRatio: CGFloat = 0.5
    let leftTab: Tab
    let rightTab: Tab

    var body: some View {
        GeometryReader { geometry in
            HStack(spacing: 1) {
                WebView(tab: leftTab)
                    .frame(width: geometry.size.width * splitRatio)

                // Draggable divider
                Rectangle()
                    .fill(.quaternary)
                    .frame(width: 4)
                    .gesture(
                        DragGesture()
                            .onChanged { value in
                                let newRatio = value.location.x / geometry.size.width
                                splitRatio = max(0.2, min(0.8, newRatio))
                            }
                    )

                WebView(tab: rightTab)
            }
        }
    }
}

Wzorzec 3: tożsamości gradientowe

Każda Space ma unikalny gradient, który zapewnia natychmiastowe wizualne rozpoznanie.

.space-gradient {
  --gradient-work: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  --gradient-personal: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
  --gradient-project: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}

.space-indicator {
  width: 100%;
  height: 4px;
  background: var(--current-gradient);
}

.space-badge {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--current-gradient);
  display: flex;
  align-items: center;
  justify-content: center;
}

Lekcje projektowe

  1. Kwestionuj założenia: Pasek kart nie jest święty; nic nie jest
  2. Separacja kontekstów: Różne zadania zasługują na różne środowiska
  3. Klawiatura na pierwszym miejscu: Zaawansowani użytkownicy nawigują bez myszy
  4. Progresywna złożoność: Domyślnie proste, zaawansowane gdy potrzeba
  5. Personalizacja przez użytkownika: Pozwól ludziom dostosować oprogramowanie do siebie
  6. Odpowiednia waga: Dopasuj złożoność interfejsu do złożoności zadania

Najczęściej zadawane pytania

Dlaczego Arc użył pionowego paska bocznego zamiast poziomych kart?

Poziome karty kurczą się do nieczytelnych rozmiarów, gdy otworzysz wiele stron. Pionowe paski boczne wykorzystują obfitą wysokość ekranu na współczesnych szerokoekranowych monitorach, zachowując pełną widoczność tytułów kart. Pasek boczny umożliwia również funkcje organizacyjne, takie jak sekcje przypięte i foldery, które nie działają dobrze poziomo.

Czym Spaces w Arc różnią się od profili przeglądarki?

Spaces są lżejsze niż pełne profile przeglądarki. Podczas gdy profile tworzą całkowicie oddzielne instancje przeglądarki (różne ciasteczka, historia, rozszerzenia), Spaces współdzielą konfigurację przeglądarki, ale organizują karty według kontekstu. Możesz natychmiast przełączać Spaces za pomocą skrótów klawiszowych, podczas gdy przełączanie profili wymaga ponownego uruchomienia lub otwarcia nowych okien.

Co dzieje się z kartami w sekcji „Today" Arc?

Karty w sekcji Today automatycznie archiwizują się po 12 godzinach nieaktywności. Nie są usuwane, ale przenoszone do przeszukiwalnego archiwum. Zapobiega to gromadzeniu kart, jednocześnie umożliwiając odzyskanie zawartości. Przypięte karty nigdy nie są automatycznie archiwizowane.

Czy mogę użyć wzorców projektowych Arc w mojej własnej aplikacji?

Tak. Wzorce udokumentowane tutaj (zwijany pasek boczny, command palette, architektura Spaces, system Boost) to koncepcje do przeniesienia. Przykłady kodu pokazują implementacje CSS, SwiftUI i TypeScript, które możesz dostosować. Wzorce paska bocznego i command bar sprawdzają się szczególnie dobrze w każdej aplikacji z dużą ilością treści.

Jak Command Bar Arc usprawnia tradycyjne paski URL?

Tradycyjne paski URL przeszukują historię i sugerują adresy URL. Command Bar Arc przeszukuje otwarte karty, historię, zakładki i dostępne akcje z jednego pola wejściowego. Możesz znaleźć otwartą kartę, wykonać polecenie lub przejść gdzieś nowego bez zmiany kontekstu lub pamiętania, gdzie coś się znajduje.


Bibliografia