reworked style tree to use colorScheme instead of old theme. Very limited style for now

K Simmons created

Change summary

Cargo.lock                                     |  17 
crates/chat_panel/Cargo.toml                   |  20 
crates/chat_panel/src/chat_panel.rs            | 433 --------------------
crates/terminal/src/mappings/colors.rs         | 130 ++---
crates/terminal/src/terminal.rs                |   2 
crates/terminal/src/terminal_container_view.rs |  12 
crates/terminal/src/terminal_element.rs        |  25 
crates/terminal/src/terminal_view.rs           |   1 
crates/theme/src/theme.rs                      |  19 
crates/zed/Cargo.toml                          |   1 
crates/zed/src/main.rs                         |   1 
styles/src/buildThemes.ts                      |  22 
styles/src/colorSchemes.ts                     |  31 +
styles/src/styleTree/app.ts                    |  42 -
styles/src/styleTree/chatPanel.ts              | 108 ----
styles/src/styleTree/commandPalette.ts         |  15 
styles/src/styleTree/components.ts             | 183 ++++---
styles/src/styleTree/contactFinder.ts          |  19 
styles/src/styleTree/contactNotification.ts    |  21 
styles/src/styleTree/contactsPanel.ts          |  67 +-
styles/src/styleTree/contextMenu.ts            |  33 
styles/src/styleTree/editor.ts                 | 219 ++++++---
styles/src/styleTree/hoverPopover.ts           |  38 -
styles/src/styleTree/picker.ts                 |  38 
styles/src/styleTree/projectDiagnostics.ts     |  11 
styles/src/styleTree/projectPanel.ts           |  27 
styles/src/styleTree/search.ts                 |  50 +-
styles/src/styleTree/statusBar.ts              |  77 +-
styles/src/styleTree/tabBar.ts                 |  52 +-
styles/src/styleTree/terminal.ts               |  79 +--
styles/src/styleTree/tooltip.ts                |  20 
styles/src/styleTree/updateNotification.ts     |  17 
styles/src/styleTree/workspace.ts              | 101 ++--
styles/src/themes.ts                           |  31 -
styles/src/themes/abruzzo.ts                   |   4 
styles/src/themes/andromeda.ts                 |   4 
styles/src/themes/brushtrees.ts                |   6 
styles/src/themes/cave.ts                      |   6 
styles/src/themes/common/base16.ts             | 288 -------------
styles/src/themes/common/colorScheme.ts        |  83 +++
styles/src/themes/common/ramps.ts              | 176 ++++++++
styles/src/themes/common/theme.ts              | 164 -------
styles/src/themes/one-dark.ts                  |   4 
styles/src/themes/one-light.ts                 |   4 
styles/src/themes/rose-pine-dawn.ts            |   4 
styles/src/themes/rose-pine-moon.ts            |   4 
styles/src/themes/rose-pine.ts                 |   4 
styles/src/themes/sandcastle.ts                |   4 
styles/src/themes/solarized.ts                 |   6 
styles/src/themes/sulphurpool.ts               |   6 
styles/src/themes/summercamp.ts                |   4 
styles/src/themes/summerfruit.ts               |   6 
styles/src/themes/template.ts                  |  10 
53 files changed, 1,016 insertions(+), 1,733 deletions(-)

Detailed changes

Cargo.lock πŸ”—

@@ -814,22 +814,6 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
-[[package]]
-name = "chat_panel"
-version = "0.1.0"
-dependencies = [
- "client",
- "editor",
- "gpui",
- "menu",
- "postage",
- "settings",
- "theme",
- "time 0.3.11",
- "util",
- "workspace",
-]
-
 [[package]]
 name = "chrono"
 version = "0.4.19"
@@ -7138,7 +7122,6 @@ dependencies = [
  "auto_update",
  "backtrace",
  "breadcrumbs",
- "chat_panel",
  "chrono",
  "cli",
  "client",

crates/chat_panel/Cargo.toml πŸ”—

@@ -1,20 +0,0 @@
-[package]
-name = "chat_panel"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-path = "src/chat_panel.rs"
-doctest = false
-
-[dependencies]
-client = { path = "../client" }
-editor = { path = "../editor" }
-gpui = { path = "../gpui" }
-menu = { path = "../menu" }
-settings = { path = "../settings" }
-theme = { path = "../theme" }
-util = { path = "../util" }
-workspace = { path = "../workspace" }
-postage = { version = "0.4.1", features = ["futures-traits"] }
-time = { version = "0.3", features = ["serde", "serde-well-known"] }

crates/chat_panel/src/chat_panel.rs πŸ”—

@@ -1,433 +0,0 @@
-use client::{
-    channel::{Channel, ChannelEvent, ChannelList, ChannelMessage},
-    Client,
-};
-use editor::Editor;
-use gpui::{
-    actions,
-    elements::*,
-    platform::CursorStyle,
-    views::{ItemType, Select, SelectStyle},
-    AnyViewHandle, AppContext, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext,
-    Subscription, Task, View, ViewContext, ViewHandle,
-};
-use menu::Confirm;
-use postage::prelude::Stream;
-use settings::{Settings, SoftWrap};
-use std::sync::Arc;
-use time::{OffsetDateTime, UtcOffset};
-use util::{ResultExt, TryFutureExt};
-
-const MESSAGE_LOADING_THRESHOLD: usize = 50;
-
-pub struct ChatPanel {
-    rpc: Arc<Client>,
-    channel_list: ModelHandle<ChannelList>,
-    active_channel: Option<(ModelHandle<Channel>, Subscription)>,
-    message_list: ListState,
-    input_editor: ViewHandle<Editor>,
-    channel_select: ViewHandle<Select>,
-    local_timezone: UtcOffset,
-    _observe_status: Task<()>,
-}
-
-pub enum Event {}
-
-actions!(chat_panel, [LoadMoreMessages]);
-
-pub fn init(cx: &mut MutableAppContext) {
-    cx.add_action(ChatPanel::send);
-    cx.add_action(ChatPanel::load_more_messages);
-}
-
-impl ChatPanel {
-    pub fn new(
-        rpc: Arc<Client>,
-        channel_list: ModelHandle<ChannelList>,
-        cx: &mut ViewContext<Self>,
-    ) -> Self {
-        let input_editor = cx.add_view(|cx| {
-            let mut editor =
-                Editor::auto_height(4, Some(|theme| theme.chat_panel.input_editor.clone()), cx);
-            editor.set_soft_wrap_mode(SoftWrap::EditorWidth, cx);
-            editor
-        });
-        let channel_select = cx.add_view(|cx| {
-            let channel_list = channel_list.clone();
-            Select::new(0, cx, {
-                move |ix, item_type, is_hovered, cx| {
-                    Self::render_channel_name(
-                        &channel_list,
-                        ix,
-                        item_type,
-                        is_hovered,
-                        &cx.global::<Settings>().theme.chat_panel.channel_select,
-                        cx,
-                    )
-                }
-            })
-            .with_style(move |cx| {
-                let theme = &cx.global::<Settings>().theme.chat_panel.channel_select;
-                SelectStyle {
-                    header: theme.header.container,
-                    menu: theme.menu,
-                }
-            })
-        });
-
-        let mut message_list = ListState::new(0, Orientation::Bottom, 1000., cx, {
-            let this = cx.weak_handle();
-            move |_, ix, cx| {
-                let this = this.upgrade(cx).unwrap().read(cx);
-                let message = this.active_channel.as_ref().unwrap().0.read(cx).message(ix);
-                this.render_message(message, cx)
-            }
-        });
-        message_list.set_scroll_handler(|visible_range, cx| {
-            if visible_range.start < MESSAGE_LOADING_THRESHOLD {
-                cx.dispatch_action(LoadMoreMessages);
-            }
-        });
-        let _observe_status = cx.spawn_weak(|this, mut cx| {
-            let mut status = rpc.status();
-            async move {
-                while (status.recv().await).is_some() {
-                    if let Some(this) = this.upgrade(&cx) {
-                        this.update(&mut cx, |_, cx| cx.notify());
-                    } else {
-                        break;
-                    }
-                }
-            }
-        });
-
-        let mut this = Self {
-            rpc,
-            channel_list,
-            active_channel: Default::default(),
-            message_list,
-            input_editor,
-            channel_select,
-            local_timezone: cx.platform().local_timezone(),
-            _observe_status,
-        };
-
-        this.init_active_channel(cx);
-        cx.observe(&this.channel_list, |this, _, cx| {
-            this.init_active_channel(cx);
-        })
-        .detach();
-        cx.observe(&this.channel_select, |this, channel_select, cx| {
-            let selected_ix = channel_select.read(cx).selected_index();
-            let selected_channel = this.channel_list.update(cx, |channel_list, cx| {
-                let available_channels = channel_list.available_channels()?;
-                let channel_id = available_channels.get(selected_ix)?.id;
-                channel_list.get_channel(channel_id, cx)
-            });
-            if let Some(selected_channel) = selected_channel {
-                this.set_active_channel(selected_channel, cx);
-            }
-        })
-        .detach();
-
-        this
-    }
-
-    fn init_active_channel(&mut self, cx: &mut ViewContext<Self>) {
-        let (active_channel, channel_count) = self.channel_list.update(cx, |list, cx| {
-            let channel_count;
-            let mut active_channel = None;
-
-            if let Some(available_channels) = list.available_channels() {
-                channel_count = available_channels.len();
-                if self.active_channel.is_none() {
-                    if let Some(channel_id) = available_channels.first().map(|channel| channel.id) {
-                        active_channel = list.get_channel(channel_id, cx);
-                    }
-                }
-            } else {
-                channel_count = 0;
-            }
-
-            (active_channel, channel_count)
-        });
-
-        if let Some(active_channel) = active_channel {
-            self.set_active_channel(active_channel, cx);
-        } else {
-            self.message_list.reset(0);
-            self.active_channel = None;
-        }
-
-        self.channel_select.update(cx, |select, cx| {
-            select.set_item_count(channel_count, cx);
-        });
-    }
-
-    fn set_active_channel(&mut self, channel: ModelHandle<Channel>, cx: &mut ViewContext<Self>) {
-        if self.active_channel.as_ref().map(|e| &e.0) != Some(&channel) {
-            {
-                let channel = channel.read(cx);
-                self.message_list.reset(channel.message_count());
-                let placeholder = format!("Message #{}", channel.name());
-                self.input_editor.update(cx, move |editor, cx| {
-                    editor.set_placeholder_text(placeholder, cx);
-                });
-            }
-            let subscription = cx.subscribe(&channel, Self::channel_did_change);
-            self.active_channel = Some((channel, subscription));
-        }
-    }
-
-    fn channel_did_change(
-        &mut self,
-        _: ModelHandle<Channel>,
-        event: &ChannelEvent,
-        cx: &mut ViewContext<Self>,
-    ) {
-        match event {
-            ChannelEvent::MessagesUpdated {
-                old_range,
-                new_count,
-            } => {
-                self.message_list.splice(old_range.clone(), *new_count);
-            }
-        }
-        cx.notify();
-    }
-
-    fn render_channel(&self, cx: &mut RenderContext<Self>) -> ElementBox {
-        let theme = &cx.global::<Settings>().theme;
-        Flex::column()
-            .with_child(
-                Container::new(ChildView::new(&self.channel_select).boxed())
-                    .with_style(theme.chat_panel.channel_select.container)
-                    .boxed(),
-            )
-            .with_child(self.render_active_channel_messages())
-            .with_child(self.render_input_box(cx))
-            .boxed()
-    }
-
-    fn render_active_channel_messages(&self) -> ElementBox {
-        let messages = if self.active_channel.is_some() {
-            List::new(self.message_list.clone()).boxed()
-        } else {
-            Empty::new().boxed()
-        };
-
-        FlexItem::new(messages).flex(1., true).boxed()
-    }
-
-    fn render_message(&self, message: &ChannelMessage, cx: &AppContext) -> ElementBox {
-        let now = OffsetDateTime::now_utc();
-        let settings = cx.global::<Settings>();
-        let theme = if message.is_pending() {
-            &settings.theme.chat_panel.pending_message
-        } else {
-            &settings.theme.chat_panel.message
-        };
-
-        Container::new(
-            Flex::column()
-                .with_child(
-                    Flex::row()
-                        .with_child(
-                            Container::new(
-                                Label::new(
-                                    message.sender.github_login.clone(),
-                                    theme.sender.text.clone(),
-                                )
-                                .boxed(),
-                            )
-                            .with_style(theme.sender.container)
-                            .boxed(),
-                        )
-                        .with_child(
-                            Container::new(
-                                Label::new(
-                                    format_timestamp(message.timestamp, now, self.local_timezone),
-                                    theme.timestamp.text.clone(),
-                                )
-                                .boxed(),
-                            )
-                            .with_style(theme.timestamp.container)
-                            .boxed(),
-                        )
-                        .boxed(),
-                )
-                .with_child(Text::new(message.body.clone(), theme.body.clone()).boxed())
-                .boxed(),
-        )
-        .with_style(theme.container)
-        .boxed()
-    }
-
-    fn render_input_box(&self, cx: &AppContext) -> ElementBox {
-        let theme = &cx.global::<Settings>().theme;
-        Container::new(ChildView::new(&self.input_editor).boxed())
-            .with_style(theme.chat_panel.input_editor.container)
-            .boxed()
-    }
-
-    fn render_channel_name(
-        channel_list: &ModelHandle<ChannelList>,
-        ix: usize,
-        item_type: ItemType,
-        is_hovered: bool,
-        theme: &theme::ChannelSelect,
-        cx: &AppContext,
-    ) -> ElementBox {
-        let channel = &channel_list.read(cx).available_channels().unwrap()[ix];
-        let theme = match (item_type, is_hovered) {
-            (ItemType::Header, _) => &theme.header,
-            (ItemType::Selected, false) => &theme.active_item,
-            (ItemType::Selected, true) => &theme.hovered_active_item,
-            (ItemType::Unselected, false) => &theme.item,
-            (ItemType::Unselected, true) => &theme.hovered_item,
-        };
-        Container::new(
-            Flex::row()
-                .with_child(
-                    Container::new(Label::new("#".to_string(), theme.hash.text.clone()).boxed())
-                        .with_style(theme.hash.container)
-                        .boxed(),
-                )
-                .with_child(Label::new(channel.name.clone(), theme.name.clone()).boxed())
-                .boxed(),
-        )
-        .with_style(theme.container)
-        .boxed()
-    }
-
-    fn render_sign_in_prompt(&self, cx: &mut RenderContext<Self>) -> ElementBox {
-        let theme = cx.global::<Settings>().theme.clone();
-        let rpc = self.rpc.clone();
-        let this = cx.handle();
-
-        enum SignInPromptLabel {}
-
-        Align::new(
-            MouseEventHandler::<SignInPromptLabel>::new(0, cx, |mouse_state, _| {
-                Label::new(
-                    "Sign in to use chat".to_string(),
-                    if mouse_state.hovered {
-                        theme.chat_panel.hovered_sign_in_prompt.clone()
-                    } else {
-                        theme.chat_panel.sign_in_prompt.clone()
-                    },
-                )
-                .boxed()
-            })
-            .with_cursor_style(CursorStyle::PointingHand)
-            .on_click(MouseButton::Left, move |_, cx| {
-                let rpc = rpc.clone();
-                let this = this.clone();
-                cx.spawn(|mut cx| async move {
-                    if rpc
-                        .authenticate_and_connect(true, &cx)
-                        .log_err()
-                        .await
-                        .is_some()
-                    {
-                        cx.update(|cx| {
-                            if let Some(this) = this.upgrade(cx) {
-                                if this.is_focused(cx) {
-                                    this.update(cx, |this, cx| cx.focus(&this.input_editor));
-                                }
-                            }
-                        })
-                    }
-                })
-                .detach();
-            })
-            .boxed(),
-        )
-        .boxed()
-    }
-
-    fn send(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
-        if let Some((channel, _)) = self.active_channel.as_ref() {
-            let body = self.input_editor.update(cx, |editor, cx| {
-                let body = editor.text(cx);
-                editor.clear(cx);
-                body
-            });
-
-            if let Some(task) = channel
-                .update(cx, |channel, cx| channel.send_message(body, cx))
-                .log_err()
-            {
-                task.detach();
-            }
-        }
-    }
-
-    fn load_more_messages(&mut self, _: &LoadMoreMessages, cx: &mut ViewContext<Self>) {
-        if let Some((channel, _)) = self.active_channel.as_ref() {
-            channel.update(cx, |channel, cx| {
-                channel.load_more_messages(cx);
-            })
-        }
-    }
-}
-
-impl Entity for ChatPanel {
-    type Event = Event;
-}
-
-impl View for ChatPanel {
-    fn ui_name() -> &'static str {
-        "ChatPanel"
-    }
-
-    fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
-        let element = if self.rpc.user_id().is_some() {
-            self.render_channel(cx)
-        } else {
-            self.render_sign_in_prompt(cx)
-        };
-        let theme = &cx.global::<Settings>().theme;
-        ConstrainedBox::new(
-            Container::new(element)
-                .with_style(theme.chat_panel.container)
-                .boxed(),
-        )
-        .with_min_width(150.)
-        .boxed()
-    }
-
-    fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
-        if matches!(
-            *self.rpc.status().borrow(),
-            client::Status::Connected { .. }
-        ) {
-            cx.focus(&self.input_editor);
-        }
-    }
-}
-
-fn format_timestamp(
-    mut timestamp: OffsetDateTime,
-    mut now: OffsetDateTime,
-    local_timezone: UtcOffset,
-) -> String {
-    timestamp = timestamp.to_offset(local_timezone);
-    now = now.to_offset(local_timezone);
-
-    let today = now.date();
-    let date = timestamp.date();
-    let mut hour = timestamp.hour();
-    let mut part = "am";
-    if hour > 12 {
-        hour -= 12;
-        part = "pm";
-    }
-    if date == today {
-        format!("{:02}:{:02}{}", hour, timestamp.minute(), part)
-    } else if date.next_day() == Some(today) {
-        format!("yesterday at {:02}:{:02}{}", hour, timestamp.minute(), part)
-    } else {
-        format!("{:02}/{}/{}", date.month() as u32, date.day(), date.year())
-    }
-}

crates/terminal/src/mappings/colors.rs πŸ”—

@@ -1,77 +1,71 @@
 use alacritty_terminal::{ansi::Color as AnsiColor, term::color::Rgb as AlacRgb};
 use gpui::color::Color;
-use theme::TerminalColors;
+use theme::TerminalStyle;
 
 ///Converts a 2, 8, or 24 bit color ANSI color to the GPUI equivalent
-pub fn convert_color(alac_color: &AnsiColor, colors: &TerminalColors, modal: bool) -> Color {
-    let background = if modal {
-        colors.modal_background
-    } else {
-        colors.background
-    };
-
+pub fn convert_color(alac_color: &AnsiColor, style: &TerminalStyle) -> Color {
     match alac_color {
         //Named and theme defined colors
         alacritty_terminal::ansi::Color::Named(n) => match n {
-            alacritty_terminal::ansi::NamedColor::Black => colors.black,
-            alacritty_terminal::ansi::NamedColor::Red => colors.red,
-            alacritty_terminal::ansi::NamedColor::Green => colors.green,
-            alacritty_terminal::ansi::NamedColor::Yellow => colors.yellow,
-            alacritty_terminal::ansi::NamedColor::Blue => colors.blue,
-            alacritty_terminal::ansi::NamedColor::Magenta => colors.magenta,
-            alacritty_terminal::ansi::NamedColor::Cyan => colors.cyan,
-            alacritty_terminal::ansi::NamedColor::White => colors.white,
-            alacritty_terminal::ansi::NamedColor::BrightBlack => colors.bright_black,
-            alacritty_terminal::ansi::NamedColor::BrightRed => colors.bright_red,
-            alacritty_terminal::ansi::NamedColor::BrightGreen => colors.bright_green,
-            alacritty_terminal::ansi::NamedColor::BrightYellow => colors.bright_yellow,
-            alacritty_terminal::ansi::NamedColor::BrightBlue => colors.bright_blue,
-            alacritty_terminal::ansi::NamedColor::BrightMagenta => colors.bright_magenta,
-            alacritty_terminal::ansi::NamedColor::BrightCyan => colors.bright_cyan,
-            alacritty_terminal::ansi::NamedColor::BrightWhite => colors.bright_white,
-            alacritty_terminal::ansi::NamedColor::Foreground => colors.foreground,
-            alacritty_terminal::ansi::NamedColor::Background => background,
-            alacritty_terminal::ansi::NamedColor::Cursor => colors.cursor,
-            alacritty_terminal::ansi::NamedColor::DimBlack => colors.dim_black,
-            alacritty_terminal::ansi::NamedColor::DimRed => colors.dim_red,
-            alacritty_terminal::ansi::NamedColor::DimGreen => colors.dim_green,
-            alacritty_terminal::ansi::NamedColor::DimYellow => colors.dim_yellow,
-            alacritty_terminal::ansi::NamedColor::DimBlue => colors.dim_blue,
-            alacritty_terminal::ansi::NamedColor::DimMagenta => colors.dim_magenta,
-            alacritty_terminal::ansi::NamedColor::DimCyan => colors.dim_cyan,
-            alacritty_terminal::ansi::NamedColor::DimWhite => colors.dim_white,
-            alacritty_terminal::ansi::NamedColor::BrightForeground => colors.bright_foreground,
-            alacritty_terminal::ansi::NamedColor::DimForeground => colors.dim_foreground,
+            alacritty_terminal::ansi::NamedColor::Black => style.black,
+            alacritty_terminal::ansi::NamedColor::Red => style.red,
+            alacritty_terminal::ansi::NamedColor::Green => style.green,
+            alacritty_terminal::ansi::NamedColor::Yellow => style.yellow,
+            alacritty_terminal::ansi::NamedColor::Blue => style.blue,
+            alacritty_terminal::ansi::NamedColor::Magenta => style.magenta,
+            alacritty_terminal::ansi::NamedColor::Cyan => style.cyan,
+            alacritty_terminal::ansi::NamedColor::White => style.white,
+            alacritty_terminal::ansi::NamedColor::BrightBlack => style.bright_black,
+            alacritty_terminal::ansi::NamedColor::BrightRed => style.bright_red,
+            alacritty_terminal::ansi::NamedColor::BrightGreen => style.bright_green,
+            alacritty_terminal::ansi::NamedColor::BrightYellow => style.bright_yellow,
+            alacritty_terminal::ansi::NamedColor::BrightBlue => style.bright_blue,
+            alacritty_terminal::ansi::NamedColor::BrightMagenta => style.bright_magenta,
+            alacritty_terminal::ansi::NamedColor::BrightCyan => style.bright_cyan,
+            alacritty_terminal::ansi::NamedColor::BrightWhite => style.bright_white,
+            alacritty_terminal::ansi::NamedColor::Foreground => style.foreground,
+            alacritty_terminal::ansi::NamedColor::Background => style.background,
+            alacritty_terminal::ansi::NamedColor::Cursor => style.cursor,
+            alacritty_terminal::ansi::NamedColor::DimBlack => style.dim_black,
+            alacritty_terminal::ansi::NamedColor::DimRed => style.dim_red,
+            alacritty_terminal::ansi::NamedColor::DimGreen => style.dim_green,
+            alacritty_terminal::ansi::NamedColor::DimYellow => style.dim_yellow,
+            alacritty_terminal::ansi::NamedColor::DimBlue => style.dim_blue,
+            alacritty_terminal::ansi::NamedColor::DimMagenta => style.dim_magenta,
+            alacritty_terminal::ansi::NamedColor::DimCyan => style.dim_cyan,
+            alacritty_terminal::ansi::NamedColor::DimWhite => style.dim_white,
+            alacritty_terminal::ansi::NamedColor::BrightForeground => style.bright_foreground,
+            alacritty_terminal::ansi::NamedColor::DimForeground => style.dim_foreground,
         },
         //'True' colors
         alacritty_terminal::ansi::Color::Spec(rgb) => Color::new(rgb.r, rgb.g, rgb.b, u8::MAX),
         //8 bit, indexed colors
-        alacritty_terminal::ansi::Color::Indexed(i) => get_color_at_index(&(*i as usize), colors),
+        alacritty_terminal::ansi::Color::Indexed(i) => get_color_at_index(&(*i as usize), style),
     }
 }
 
 ///Converts an 8 bit ANSI color to it's GPUI equivalent.
 ///Accepts usize for compatability with the alacritty::Colors interface,
 ///Other than that use case, should only be called with values in the [0,255] range
-pub fn get_color_at_index(index: &usize, colors: &TerminalColors) -> Color {
+pub fn get_color_at_index(index: &usize, style: &TerminalStyle) -> Color {
     match index {
         //0-15 are the same as the named colors above
-        0 => colors.black,
-        1 => colors.red,
-        2 => colors.green,
-        3 => colors.yellow,
-        4 => colors.blue,
-        5 => colors.magenta,
-        6 => colors.cyan,
-        7 => colors.white,
-        8 => colors.bright_black,
-        9 => colors.bright_red,
-        10 => colors.bright_green,
-        11 => colors.bright_yellow,
-        12 => colors.bright_blue,
-        13 => colors.bright_magenta,
-        14 => colors.bright_cyan,
-        15 => colors.bright_white,
+        0 => style.black,
+        1 => style.red,
+        2 => style.green,
+        3 => style.yellow,
+        4 => style.blue,
+        5 => style.magenta,
+        6 => style.cyan,
+        7 => style.white,
+        8 => style.bright_black,
+        9 => style.bright_red,
+        10 => style.bright_green,
+        11 => style.bright_yellow,
+        12 => style.bright_blue,
+        13 => style.bright_magenta,
+        14 => style.bright_cyan,
+        15 => style.bright_white,
         //16-231 are mapped to their RGB colors on a 0-5 range per channel
         16..=231 => {
             let (r, g, b) = rgb_for_index(&(*index as u8)); //Split the index into it's ANSI-RGB components
@@ -85,19 +79,19 @@ pub fn get_color_at_index(index: &usize, colors: &TerminalColors) -> Color {
             Color::new(i * step, i * step, i * step, u8::MAX) //Map the ANSI-grayscale components to the RGB-grayscale
         }
         //For compatability with the alacritty::Colors interface
-        256 => colors.foreground,
-        257 => colors.background,
-        258 => colors.cursor,
-        259 => colors.dim_black,
-        260 => colors.dim_red,
-        261 => colors.dim_green,
-        262 => colors.dim_yellow,
-        263 => colors.dim_blue,
-        264 => colors.dim_magenta,
-        265 => colors.dim_cyan,
-        266 => colors.dim_white,
-        267 => colors.bright_foreground,
-        268 => colors.black, //'Dim Background', non-standard color
+        256 => style.foreground,
+        257 => style.background,
+        258 => style.cursor,
+        259 => style.dim_black,
+        260 => style.dim_red,
+        261 => style.dim_green,
+        262 => style.dim_yellow,
+        263 => style.dim_blue,
+        264 => style.dim_magenta,
+        265 => style.dim_cyan,
+        266 => style.dim_white,
+        267 => style.bright_foreground,
+        268 => style.black, //'Dim Background', non-standard color
         _ => Color::new(0, 0, 0, 255),
     }
 }

crates/terminal/src/terminal.rs πŸ”—

@@ -569,7 +569,7 @@ impl Terminal {
             InternalEvent::ColorRequest(index, format) => {
                 let color = term.colors()[*index].unwrap_or_else(|| {
                     let term_style = &cx.global::<Settings>().theme.terminal;
-                    to_alac_rgb(get_color_at_index(index, &term_style.colors))
+                    to_alac_rgb(get_color_at_index(index, &term_style))
                 });
                 self.write_to_pty(format(color))
             }

crates/terminal/src/terminal_container_view.rs πŸ”—

@@ -165,18 +165,12 @@ impl View for TerminalContainer {
         "Terminal"
     }
 
-    fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
-        let child_view = match &self.content {
+    fn render(&mut self, _cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox {
+        match &self.content {
             TerminalContainerContent::Connected(connected) => ChildView::new(connected),
             TerminalContainerContent::Error(error) => ChildView::new(error),
-        };
-        if self.modal {
-            let settings = cx.global::<Settings>();
-            let container_style = settings.theme.terminal.modal_container;
-            child_view.contained().with_style(container_style).boxed()
-        } else {
-            child_view.boxed()
         }
+        .boxed()
     }
 
     fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {

crates/terminal/src/terminal_element.rs πŸ”—

@@ -149,7 +149,6 @@ impl LayoutRect {
 pub struct TerminalElement {
     terminal: WeakModelHandle<Terminal>,
     view: WeakViewHandle<TerminalView>,
-    modal: bool,
     focused: bool,
     cursor_visible: bool,
 }
@@ -158,14 +157,12 @@ impl TerminalElement {
     pub fn new(
         view: WeakViewHandle<TerminalView>,
         terminal: WeakModelHandle<Terminal>,
-        modal: bool,
         focused: bool,
         cursor_visible: bool,
     ) -> TerminalElement {
         TerminalElement {
             view,
             terminal,
-            modal,
             focused,
             cursor_visible,
         }
@@ -179,7 +176,6 @@ impl TerminalElement {
         terminal_theme: &TerminalStyle,
         text_layout_cache: &TextLayoutCache,
         font_cache: &FontCache,
-        modal: bool,
     ) -> (Vec<LayoutCell>, Vec<LayoutRect>) {
         let mut cells = vec![];
         let mut rects = vec![];
@@ -218,7 +214,7 @@ impl TerminalElement {
                                     cur_rect = Some(LayoutRect::new(
                                         Point::new(line_index as i32, cell.point.column.0 as i32),
                                         1,
-                                        convert_color(&bg, &terminal_theme.colors, modal),
+                                        convert_color(&bg, &terminal_theme),
                                     ));
                                 }
                             }
@@ -227,7 +223,7 @@ impl TerminalElement {
                                 cur_rect = Some(LayoutRect::new(
                                     Point::new(line_index as i32, cell.point.column.0 as i32),
                                     1,
-                                    convert_color(&bg, &terminal_theme.colors, modal),
+                                    convert_color(&bg, &terminal_theme),
                                 ));
                             }
                         }
@@ -244,7 +240,6 @@ impl TerminalElement {
                             terminal_theme,
                             text_style,
                             font_cache,
-                            modal,
                         );
 
                         let layout_cell = text_layout_cache.layout_str(
@@ -303,10 +298,9 @@ impl TerminalElement {
         style: &TerminalStyle,
         text_style: &TextStyle,
         font_cache: &FontCache,
-        modal: bool,
     ) -> RunStyle {
         let flags = indexed.cell.flags;
-        let fg = convert_color(&fg, &style.colors, modal);
+        let fg = convert_color(&fg, &style);
 
         let underline = flags
             .intersects(Flags::ALL_UNDERLINES)
@@ -562,11 +556,7 @@ impl Element for TerminalElement {
             Default::default()
         };
 
-        let background_color = if self.modal {
-            terminal_theme.colors.modal_background
-        } else {
-            terminal_theme.colors.background
-        };
+        let background_color = terminal_theme.background;
         let terminal_handle = self.terminal.upgrade(cx).unwrap();
 
         terminal_handle.update(cx.app, |terminal, cx| {
@@ -601,7 +591,6 @@ impl Element for TerminalElement {
             &terminal_theme,
             cx.text_layout_cache,
             cx.font_cache(),
-            self.modal,
         );
 
         //Layout cursor. Rectangle is used for IME, so we should lay it out even
@@ -614,9 +603,9 @@ impl Element for TerminalElement {
                 let str_trxt = cursor_char.to_string();
 
                 let color = if self.focused {
-                    terminal_theme.colors.background
+                    terminal_theme.background
                 } else {
-                    terminal_theme.colors.foreground
+                    terminal_theme.foreground
                 };
 
                 cx.text_layout_cache.layout_str(
@@ -649,7 +638,7 @@ impl Element for TerminalElement {
                         cursor_position,
                         block_width,
                         dimensions.line_height,
-                        terminal_theme.colors.cursor,
+                        terminal_theme.cursor,
                         shape,
                         Some(cursor_text),
                     )

crates/theme/src/theme.rs πŸ”—

@@ -18,7 +18,6 @@ pub struct Theme {
     pub meta: ThemeMeta,
     pub workspace: Workspace,
     pub context_menu: ContextMenu,
-    pub chat_panel: ChatPanel,
     pub contacts_popover: ContactsPopover,
     pub contacts_panel: ContactsPanel,
     pub contact_finder: ContactFinder,
@@ -256,18 +255,6 @@ pub struct SidebarItem {
     pub icon_size: f32,
 }
 
-#[derive(Deserialize, Default)]
-pub struct ChatPanel {
-    #[serde(flatten)]
-    pub container: ContainerStyle,
-    pub message: ChatMessage,
-    pub pending_message: ChatMessage,
-    pub channel_select: ChannelSelect,
-    pub input_editor: FieldEditor,
-    pub sign_in_prompt: TextStyle,
-    pub hovered_sign_in_prompt: TextStyle,
-}
-
 #[derive(Deserialize, Default)]
 pub struct ProjectPanel {
     #[serde(flatten)]
@@ -710,12 +697,6 @@ pub struct HoverPopover {
 
 #[derive(Clone, Deserialize, Default)]
 pub struct TerminalStyle {
-    pub colors: TerminalColors,
-    pub modal_container: ContainerStyle,
-}
-
-#[derive(Clone, Deserialize, Default)]
-pub struct TerminalColors {
     pub black: Color,
     pub red: Color,
     pub green: Color,

crates/zed/Cargo.toml πŸ”—

@@ -19,7 +19,6 @@ activity_indicator = { path = "../activity_indicator" }
 assets = { path = "../assets" }
 auto_update = { path = "../auto_update" }
 breadcrumbs = { path = "../breadcrumbs" }
-chat_panel = { path = "../chat_panel" }
 cli = { path = "../cli" }
 collections = { path = "../collections" }
 command_palette = { path = "../command_palette" }

crates/zed/src/main.rs πŸ”—

@@ -111,7 +111,6 @@ fn main() {
         editor::init(cx);
         go_to_line::init(cx);
         file_finder::init(cx);
-        chat_panel::init(cx);
         contacts_panel::init(cx);
         outline::init(cx);
         project_symbols::init(cx);

styles/src/buildThemes.ts πŸ”—

@@ -2,9 +2,9 @@ import * as fs from "fs";
 import * as path from "path";
 import { tmpdir } from "os";
 import app from "./styleTree/app";
-import themes, { internalThemes, experimentalThemes } from "./themes";
+import colorSchemes, { internalColorSchemes, experimentalColorSchemes } from "./colorSchemes";
 import snakeCase from "./utils/snakeCase";
-import Theme from "./themes/common/theme";
+import { ColorScheme } from "./themes/common/colorScheme";
 
 const themeDirectory = `${__dirname}/../../assets/themes`;
 const internalDirectory = `${themeDirectory}/internal`;
@@ -16,7 +16,7 @@ function clearThemes(themeDirectory: string) {
   for (const file of fs.readdirSync(themeDirectory)) {
     if (file.endsWith(".json")) {
       const name = file.replace(/\.json$/, "");
-      if (!themes.find((theme) => theme.name === name)) {
+      if (!colorSchemes.find((colorScheme) => colorScheme.name === name)) {
         fs.unlinkSync(path.join(themeDirectory, file));
       }
     }
@@ -27,12 +27,12 @@ clearThemes(themeDirectory);
 clearThemes(internalDirectory);
 clearThemes(experimentsDirectory);
 
-function writeThemes(themes: Theme[], outputDirectory: string) {
-  for (let theme of themes) {
-    let styleTree = snakeCase(app(theme));
+function writeThemes(colorSchemes: ColorScheme[], outputDirectory: string) {
+  for (let colorScheme of colorSchemes) {
+    let styleTree = snakeCase(app(colorScheme));
     let styleTreeJSON = JSON.stringify(styleTree, null, 2);
-    let tempPath = path.join(tempDirectory, `${theme.name}.json`);
-    let outPath = path.join(outputDirectory, `${theme.name}.json`);
+    let tempPath = path.join(tempDirectory, `${colorScheme.name}.json`);
+    let outPath = path.join(outputDirectory, `${colorScheme.name}.json`);
     fs.writeFileSync(tempPath, styleTreeJSON);
     fs.renameSync(tempPath, outPath);
     console.log(`- ${outPath} created`);
@@ -40,6 +40,6 @@ function writeThemes(themes: Theme[], outputDirectory: string) {
 }
 
 // Write new themes to theme directory
-writeThemes(themes, themeDirectory);
-writeThemes(internalThemes, internalDirectory);
-writeThemes(experimentalThemes, experimentsDirectory);
+writeThemes(colorSchemes, themeDirectory);
+writeThemes(internalColorSchemes, internalDirectory);
+writeThemes(experimentalColorSchemes, experimentsDirectory);

styles/src/colorSchemes.ts πŸ”—

@@ -0,0 +1,31 @@
+import fs from "fs";
+import path from "path";
+import { ColorScheme } from "./themes/common/colorScheme";
+
+const colorSchemes: ColorScheme[] = [];
+export default colorSchemes;
+
+const internalColorSchemes: ColorScheme[] = [];
+export { internalColorSchemes }
+
+const experimentalColorSchemes: ColorScheme[] = [];
+export { experimentalColorSchemes }
+
+
+function fillColorSchemes(themesPath: string, colorSchemes: ColorScheme[]) {
+  for (const fileName of fs.readdirSync(themesPath)) {
+    if (fileName == "template.ts") continue;
+    const filePath = path.join(themesPath, fileName);
+
+    if (fs.statSync(filePath).isFile()) {
+      const colorScheme = require(filePath);
+      if (colorScheme.dark) colorSchemes.push(colorScheme.dark);
+      if (colorScheme.light) colorSchemes.push(colorScheme.light);
+    }
+  }
+}
+
+fillColorSchemes(path.resolve(`${__dirname}/themes`), colorSchemes)
+fillColorSchemes(path.resolve(`${__dirname}/themes/internal`), internalColorSchemes)
+fillColorSchemes(path.resolve(`${__dirname}/themes/experiments`), experimentalColorSchemes)
+

styles/src/styleTree/app.ts πŸ”—

@@ -1,5 +1,3 @@
-import Theme from "../themes/common/theme";
-import chatPanel from "./chatPanel";
 import { text } from "./components";
 import contactFinder from "./contactFinder";
 import contactsPanel from "./contactsPanel";
@@ -16,38 +14,38 @@ import contactNotification from "./contactNotification";
 import updateNotification from "./updateNotification";
 import tooltip from "./tooltip";
 import terminal from "./terminal";
+import { ColorScheme } from "../themes/common/colorScheme";
 
 export const panel = {
   padding: { top: 12, bottom: 12 },
 };
 
-export default function app(theme: Theme): Object {
+export default function app(colorScheme: ColorScheme): Object {
   return {
     meta: {
-      name: theme.name,
-      isLight: theme.isLight
+      name: colorScheme.name,
+      isLight: colorScheme.isLight
     },
-    picker: picker(theme),
-    workspace: workspace(theme),
-    contextMenu: contextMenu(theme),
-    editor: editor(theme),
-    projectDiagnostics: projectDiagnostics(theme),
-    commandPalette: commandPalette(theme),
-    projectPanel: projectPanel(theme),
-    chatPanel: chatPanel(theme),
-    contactsPopover: contactsPopover(theme),
-    contactsPanel: contactsPanel(theme),
-    contactFinder: contactFinder(theme),
-    search: search(theme),
+    picker: picker(colorScheme),
+    workspace: workspace(colorScheme),
+    contextMenu: contextMenu(colorScheme),
+    editor: editor(colorScheme),
+    projectDiagnostics: projectDiagnostics(colorScheme),
+    commandPalette: commandPalette(colorScheme),
+    projectPanel: projectPanel(colorScheme),
+    contactsPopover: contactsPopover(colorScheme),
+    contactsPanel: contactsPanel(colorScheme),
+    contactFinder: contactFinder(colorScheme),
+    search: search(colorScheme),
     breadcrumbs: {
-      ...text(theme, "sans", "secondary"),
+      ...text(colorScheme.lowest.top, "sans", "base", "variant"),
       padding: {
         left: 6,
       },
     },
-    contactNotification: contactNotification(theme),
-    updateNotification: updateNotification(theme),
-    tooltip: tooltip(theme),
-    terminal: terminal(theme),
+    contactNotification: contactNotification(colorScheme),
+    updateNotification: updateNotification(colorScheme),
+    tooltip: tooltip(colorScheme),
+    terminal: terminal(colorScheme.lowest),
   };
 }

styles/src/styleTree/chatPanel.ts πŸ”—

@@ -1,108 +0,0 @@
-import Theme from "../themes/common/theme";
-import { panel } from "./app";
-import {
-  backgroundColor,
-  border,
-  player,
-  text,
-  TextColor,
-  popoverShadow,
-} from "./components";
-
-export default function chatPanel(theme: Theme) {
-  function channelSelectItem(
-    theme: Theme,
-    textColor: TextColor,
-    hovered: boolean
-  ) {
-    return {
-      name: text(theme, "sans", textColor),
-      padding: 4,
-      hash: {
-        ...text(theme, "sans", "muted"),
-        margin: {
-          right: 8,
-        },
-      },
-      background: hovered ? backgroundColor(theme, 300, "hovered") : undefined,
-      cornerRadius: hovered ? 6 : 0,
-    };
-  }
-
-  const message = {
-    body: text(theme, "sans", "secondary"),
-    timestamp: text(theme, "sans", "muted", { size: "sm" }),
-    padding: {
-      bottom: 6,
-    },
-    sender: {
-      ...text(theme, "sans", "primary", { weight: "bold" }),
-      margin: {
-        right: 8,
-      },
-    },
-  };
-
-  return {
-    ...panel,
-    channelName: text(theme, "sans", "primary", { weight: "bold" }),
-    channelNameHash: {
-      ...text(theme, "sans", "muted"),
-      padding: {
-        right: 8,
-      },
-    },
-    channelSelect: {
-      header: {
-        ...channelSelectItem(theme, "primary", false),
-        padding: {
-          bottom: 4,
-          left: 0,
-        },
-      },
-      item: channelSelectItem(theme, "secondary", false),
-      hoveredItem: channelSelectItem(theme, "secondary", true),
-      activeItem: channelSelectItem(theme, "primary", false),
-      hoveredActiveItem: channelSelectItem(theme, "primary", true),
-      menu: {
-        background: backgroundColor(theme, 500),
-        cornerRadius: 6,
-        padding: 4,
-        border: border(theme, "primary"),
-        shadow: popoverShadow(theme),
-      },
-    },
-    signInPrompt: text(theme, "sans", "secondary", { underline: true }),
-    hoveredSignInPrompt: text(theme, "sans", "primary", { underline: true }),
-    message,
-    pendingMessage: {
-      ...message,
-      body: {
-        ...message.body,
-        color: theme.textColor.muted,
-      },
-      sender: {
-        ...message.sender,
-        color: theme.textColor.muted,
-      },
-      timestamp: {
-        ...message.timestamp,
-        color: theme.textColor.muted,
-      },
-    },
-    inputEditor: {
-      background: backgroundColor(theme, 500),
-      cornerRadius: 6,
-      text: text(theme, "mono", "primary"),
-      placeholderText: text(theme, "mono", "placeholder", { size: "sm" }),
-      selection: player(theme, 1).selection,
-      border: border(theme, "secondary"),
-      padding: {
-        bottom: 7,
-        left: 8,
-        right: 8,
-        top: 7,
-      },
-    },
-  };
-}

styles/src/styleTree/commandPalette.ts πŸ”—

@@ -1,14 +1,15 @@
-import Theme from "../themes/common/theme";
-import { text, backgroundColor, border } from "./components";
+import { ColorScheme } from "../themes/common/colorScheme";
+import { text, border, background } from "./components";
 
-export default function commandPalette(theme: Theme) {
+export default function commandPalette(colorScheme: ColorScheme) {
+  let layer = colorScheme.highest.bottom;
   return {
     keystrokeSpacing: 8,
     key: {
-      text: text(theme, "mono", "secondary", { size: "xs" }),
+      text: text(layer, "mono", { size: "xs" }),
       cornerRadius: 4,
-      background: backgroundColor(theme, "on300"),
-      border: border(theme, "secondary"),
+      background: background(layer, "on"),
+      border: border(layer),
       padding: {
         top: 2,
         bottom: 2,
@@ -19,7 +20,7 @@ export default function commandPalette(theme: Theme) {
         left: 2,
       },
       active: {
-        text: text(theme, "mono", "active", { size: "xs" }),
+        text: text(layer, "mono", "on", "active", { size: "xs" }),
       },
     },
   };

styles/src/styleTree/components.ts πŸ”—

@@ -1,30 +1,89 @@
-import Theme, { BackgroundColorSet } from "../themes/common/theme";
 import { fontFamilies, fontSizes, FontWeight } from "../common";
+import { Layer, Styles, StyleSets } from "../themes/common/colorScheme";
+
+export function background(layer: Layer, styleSet?: StyleSets, style?: Styles): string {
+  return layer[styleSet ?? "base"][style ?? "default"].background;
+}
+export function borderColor(layer: Layer, styleSet?: StyleSets, style?: Styles): string {
+  return layer[styleSet ?? "base"][style ?? "default"].border;
+}
+export function foreground(layer: Layer, styleSet?: StyleSets, style?: Styles): string {
+  return layer[styleSet ?? "base"][style ?? "default"].foreground;
+}
+
+
+interface Text {
+  family: keyof typeof fontFamilies,
+  color: string,
+  size: number,
+  weight?: FontWeight,
+  underline?: boolean,
+}
+
+interface TextProperties {
+  size?: keyof typeof fontSizes;
+  weight?: FontWeight;
+  underline?: boolean;
+}
 
-export type TextColor = keyof Theme["textColor"];
 export function text(
-  theme: Theme,
+  layer: Layer,
   fontFamily: keyof typeof fontFamilies,
-  color: TextColor,
-  properties?: {
-    size?: keyof typeof fontSizes;
-    weight?: FontWeight;
-    underline?: boolean;
-  }
+  styleSet: StyleSets,
+  style: Styles,
+  properties?: TextProperties
+): Text;
+export function text(
+  layer: Layer,
+  fontFamily: keyof typeof fontFamilies,
+  styleSet: StyleSets,
+  properties?: TextProperties): Text;
+export function text(
+  layer: Layer,
+  fontFamily: keyof typeof fontFamilies,
+  properties?: TextProperties): Text;
+export function text(
+  layer: Layer,
+  fontFamily: keyof typeof fontFamilies,
+  styleSetOrProperties?: StyleSets | TextProperties,
+  styleOrProperties?: Styles | TextProperties,
+  properties?: TextProperties
 ) {
+  let styleSet: StyleSets = "base";
+  let style: Styles = "default";
+
+  if (typeof styleSetOrProperties === "string") {
+    styleSet = styleSetOrProperties
+  } else if (styleSetOrProperties !== undefined) {
+    properties = styleSetOrProperties;
+  }
+
+  if (typeof styleOrProperties === "string") {
+    style = styleOrProperties;
+  } else if (styleOrProperties !== undefined) {
+    properties = styleOrProperties;
+  }
+
   let size = fontSizes[properties?.size || "sm"];
   return {
     family: fontFamilies[fontFamily],
-    color: theme.textColor[color],
+    color: layer[styleSet][style].foreground,
     ...properties,
     size,
   };
 }
-export function textColor(theme: Theme, color: TextColor) {
-  return theme.textColor[color];
+
+
+export interface Border {
+  color: string,
+  width: number,
+  top?: boolean;
+  bottom?: boolean;
+  left?: boolean;
+  right?: boolean;
+  overlay?: boolean;
 }
 
-export type BorderColor = keyof Theme["borderColor"];
 export interface BorderOptions {
   width?: number;
   top?: boolean;
@@ -33,72 +92,46 @@ export interface BorderOptions {
   right?: boolean;
   overlay?: boolean;
 }
+
 export function border(
-  theme: Theme,
-  color: BorderColor,
+  layer: Layer,
+  styleSet: StyleSets,
+  style: Styles,
   options?: BorderOptions
-) {
-  return {
-    color: borderColor(theme, color),
-    width: 1,
-    ...options,
-  };
-}
-export function borderColor(theme: Theme, color: BorderColor) {
-  return theme.borderColor[color];
-}
-
-export type IconColor = keyof Theme["iconColor"];
-export function iconColor(theme: Theme, color: IconColor) {
-  return theme.iconColor[color];
-}
-
-export type PlayerIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
-export interface Player {
-  selection: {
-    cursor: string;
-    selection: string;
-  };
-}
-export function player(theme: Theme, playerNumber: PlayerIndex): Player {
-  return {
-    selection: {
-      cursor: theme.player[playerNumber].cursorColor,
-      selection: theme.player[playerNumber].selectionColor,
-    },
-  };
-}
-
-export type BackgroundColor = keyof Theme["backgroundColor"];
-export type BackgroundState = keyof BackgroundColorSet;
-export function backgroundColor(
-  theme: Theme,
-  name: BackgroundColor,
-  state?: BackgroundState
-): string {
-  return theme.backgroundColor[name][state || "base"];
-}
+): Border;
+export function border(
+  layer: Layer,
+  styleSet: StyleSets,
+  options?: BorderOptions
+): Border;
+export function border(
+  layer: Layer,
+  options?: BorderOptions
+): Border;
+export function border(
+  layer: Layer,
+  styleSetOrOptions?: StyleSets | BorderOptions,
+  styleOrOptions?: Styles | BorderOptions,
+  options?: BorderOptions
+): Border {
+  let styleSet: StyleSets = "base";
+  let style: Styles = "default";
 
-export function modalShadow(theme: Theme) {
-  return {
-    blur: 16,
-    color: theme.shadow,
-    offset: [0, 2],
-  };
-}
+  if (typeof styleSetOrOptions === "string") {
+    styleSet = styleSetOrOptions
+  } else if (styleSetOrOptions !== undefined) {
+    options = styleSetOrOptions;
+  }
 
-export function popoverShadow(theme: Theme) {
-  return {
-    blur: 4,
-    color: theme.shadow,
-    offset: [1, 2],
-  };
-}
+  if (typeof styleOrOptions === "string") {
+    style = styleOrOptions;
+  } else if (styleOrOptions !== undefined) {
+    options = styleOrOptions;
+  }
 
-export function draggedShadow(theme: Theme) {
   return {
-    blur: 6,
-    color: theme.shadow,
-    offset: [1, 2],
+    color: layer[styleSet][style].border,
+    width: 1,
+    ...options,
   };
-}
+}

styles/src/styleTree/contactFinder.ts πŸ”—

@@ -1,18 +1,19 @@
-import Theme from "../themes/common/theme";
 import picker from "./picker";
-import { backgroundColor, iconColor } from "./components";
+import { ColorScheme } from "../themes/common/colorScheme";
+import { background, foreground } from "./components";
 
-export default function contactFinder(theme: Theme) {
+export default function contactFinder(colorScheme: ColorScheme) {
+  let layer = colorScheme.middle.bottom;
   const contactButton = {
-    background: backgroundColor(theme, 100),
-    color: iconColor(theme, "primary"),
+    background: background(layer),
+    color: foreground(layer),
     iconWidth: 8,
     buttonWidth: 16,
     cornerRadius: 8,
   };
 
   return {
-    ...picker(theme),
+    ...picker(colorScheme),
     rowHeight: 28,
     contactAvatar: {
       cornerRadius: 10,
@@ -26,13 +27,13 @@ export default function contactFinder(theme: Theme) {
     contactButton: {
       ...contactButton,
       hover: {
-        background: backgroundColor(theme, 100, "hovered"),
+        background: background(layer, "base", "hovered"),
       },
     },
     disabledContactButton: {
       ...contactButton,
-      background: backgroundColor(theme, 100),
-      color: iconColor(theme, "muted"),
+      background: background(layer, "base", "disabled"),
+      color: foreground(layer, "base", "disabled"),
     },
   };
 }

styles/src/styleTree/contactNotification.ts πŸ”—

@@ -1,10 +1,11 @@
-import Theme from "../themes/common/theme";
-import { backgroundColor, iconColor, text } from "./components";
+import { ColorScheme } from "../themes/common/colorScheme";
+import { background, foreground, text } from "./components";
 
 const avatarSize = 12;
 const headerPadding = 8;
 
-export default function contactNotification(theme: Theme): Object {
+export default function contactNotification(colorScheme: ColorScheme): Object {
+  let layer = colorScheme.middle.bottom;
   return {
     headerAvatar: {
       height: avatarSize,
@@ -12,32 +13,32 @@ export default function contactNotification(theme: Theme): Object {
       cornerRadius: 6,
     },
     headerMessage: {
-      ...text(theme, "sans", "primary", { size: "xs" }),
+      ...text(layer, "sans", { size: "xs" }),
       margin: { left: headerPadding, right: headerPadding },
     },
     headerHeight: 18,
     bodyMessage: {
-      ...text(theme, "sans", "secondary", { size: "xs" }),
+      ...text(layer, "sans", { size: "xs" }),
       margin: { left: avatarSize + headerPadding, top: 6, bottom: 6 },
     },
     button: {
-      ...text(theme, "sans", "primary", { size: "xs" }),
-      background: backgroundColor(theme, "on300"),
+      ...text(layer, "sans", "on", { size: "xs" }),
+      background: background(layer, "on"),
       padding: 4,
       cornerRadius: 6,
       margin: { left: 6 },
       hover: {
-        background: backgroundColor(theme, "on300", "hovered"),
+        background: background(layer, "on", "hovered"),
       },
     },
     dismissButton: {
-      color: iconColor(theme, "secondary"),
+      color: foreground(layer, "on"),
       iconWidth: 8,
       iconHeight: 8,
       buttonWidth: 8,
       buttonHeight: 8,
       hover: {
-        color: iconColor(theme, "primary"),
+        color: foreground(layer, "on", "hovered"),
       },
     },
   };

styles/src/styleTree/contactsPanel.ts πŸ”—

@@ -1,18 +1,19 @@
-import Theme from "../themes/common/theme";
+import { ColorScheme } from "../themes/common/colorScheme";
 import { panel } from "./app";
 import {
-  backgroundColor,
+  background,
   border,
   borderColor,
-  iconColor,
-  player,
+  foreground,
   text,
 } from "./components";
 
-export default function contactsPanel(theme: Theme) {
+export default function contactsPanel(colorScheme: ColorScheme) {
   const nameMargin = 8;
   const sidePadding = 12;
 
+  let layer = colorScheme.lowest.middle;
+
   const projectRow = {
     guestAvatarSpacing: 4,
     height: 24,
@@ -21,7 +22,7 @@ export default function contactsPanel(theme: Theme) {
       width: 14,
     },
     name: {
-      ...text(theme, "mono", "placeholder", { size: "sm" }),
+      ...text(layer, "mono", { size: "sm" }),
       margin: {
         left: nameMargin,
         right: 6,
@@ -40,8 +41,8 @@ export default function contactsPanel(theme: Theme) {
   };
 
   const contactButton = {
-    background: backgroundColor(theme, 100),
-    color: iconColor(theme, "primary"),
+    background: background(layer, "on"),
+    color: foreground(layer, "on"),
     iconWidth: 8,
     buttonWidth: 16,
     cornerRadius: 8,
@@ -51,12 +52,12 @@ export default function contactsPanel(theme: Theme) {
     ...panel,
     padding: { top: panel.padding.top, bottom: 0 },
     userQueryEditor: {
-      background: backgroundColor(theme, 500),
+      background: background(layer, "on"),
       cornerRadius: 6,
-      text: text(theme, "mono", "primary"),
-      placeholderText: text(theme, "mono", "placeholder", { size: "sm" }),
-      selection: player(theme, 1).selection,
-      border: border(theme, "secondary"),
+      text: text(layer, "mono", "on"),
+      placeholderText: text(layer, "mono", "on", "disabled", { size: "sm" }),
+      selection: colorScheme.players[0],
+      border: border(layer, "on"),
       padding: {
         bottom: 4,
         left: 8,
@@ -71,28 +72,28 @@ export default function contactsPanel(theme: Theme) {
     userQueryEditorHeight: 32,
     addContactButton: {
       margin: { left: 6, right: 12 },
-      color: iconColor(theme, "primary"),
+      color: foreground(layer, "on"),
       buttonWidth: 16,
       iconWidth: 16,
     },
     privateButton: {
       iconWidth: 12,
-      color: iconColor(theme, "primary"),
+      color: foreground(layer, "on"),
       cornerRadius: 5,
       buttonWidth: 12,
     },
     rowHeight: 28,
     sectionIconSize: 8,
     headerRow: {
-      ...text(theme, "mono", "secondary", { size: "sm" }),
+      ...text(layer, "mono", { size: "sm" }),
       margin: { top: 14 },
       padding: {
         left: sidePadding,
         right: sidePadding,
       },
       active: {
-        ...text(theme, "mono", "primary", { size: "sm" }),
-        background: backgroundColor(theme, 100, "active"),
+        ...text(layer, "mono", "base", "active", { size: "sm" }),
+        background: background(layer, "base", "active"),
       },
     },
     contactRow: {
@@ -101,17 +102,17 @@ export default function contactsPanel(theme: Theme) {
         right: sidePadding,
       },
       active: {
-        background: backgroundColor(theme, 100, "active"),
+        background: background(layer, "base", "active"),
       },
     },
     treeBranch: {
-      color: borderColor(theme, "active"),
+      color: borderColor(layer),
       width: 1,
       hover: {
-        color: borderColor(theme, "active"),
+        color: borderColor(layer, "base", "hovered"),
       },
       active: {
-        color: borderColor(theme, "active"),
+        color: borderColor(layer, "base", "active"),
       },
     },
     contactAvatar: {
@@ -119,7 +120,7 @@ export default function contactsPanel(theme: Theme) {
       width: 18,
     },
     contactUsername: {
-      ...text(theme, "mono", "primary", { size: "sm" }),
+      ...text(layer, "mono", { size: "sm" }),
       margin: {
         left: nameMargin,
       },
@@ -128,26 +129,26 @@ export default function contactsPanel(theme: Theme) {
     contactButton: {
       ...contactButton,
       hover: {
-        background: backgroundColor(theme, "on300", "hovered"),
+        background: background(layer, "base", "hovered"),
       },
     },
     disabledButton: {
       ...contactButton,
-      background: backgroundColor(theme, 100),
-      color: iconColor(theme, "muted"),
+      background: background(layer, "on"),
+      color: foreground(layer, "on"),
     },
     projectRow: {
       ...projectRow,
-      background: backgroundColor(theme, 300),
+      background: background(layer, "on"),
       name: {
         ...projectRow.name,
-        ...text(theme, "mono", "secondary", { size: "sm" }),
+        ...text(layer, "mono", { size: "sm" }),
       },
       hover: {
-        background: backgroundColor(theme, 300, "hovered"),
+        background: background(layer, "base", "hovered"),
       },
       active: {
-        background: backgroundColor(theme, 300, "active"),
+        background: background(layer, "base", "active"),
       },
     },
     inviteRow: {
@@ -155,10 +156,10 @@ export default function contactsPanel(theme: Theme) {
         left: sidePadding,
         right: sidePadding,
       },
-      border: { top: true, width: 1, color: borderColor(theme, "primary") },
-      text: text(theme, "sans", "secondary", { size: "sm" }),
+      border: border(layer, { top: true }),
+      text: text(layer, "sans", { size: "sm" }),
       hover: {
-        text: text(theme, "sans", "active", { size: "sm" }),
+        text: text(layer, "sans", "base", "hovered", { size: "sm" }),
       },
     },
   };

styles/src/styleTree/contextMenu.ts πŸ”—

@@ -1,45 +1,46 @@
-import Theme from "../themes/common/theme";
+import { ColorScheme } from "../themes/common/colorScheme";
 import {
-  backgroundColor,
+  background,
   border,
   borderColor,
-  popoverShadow,
   text,
 } from "./components";
 
-export default function contextMenu(theme: Theme) {
+export default function contextMenu(colorScheme: ColorScheme) {
+  let elevation = colorScheme.middle;
+  let layer = elevation.bottom;
   return {
-    background: backgroundColor(theme, 300, "base"),
+    background: background(layer),
     cornerRadius: 6,
     padding: 6,
-    shadow: popoverShadow(theme),
-    border: border(theme, "primary"),
+    shadow: elevation.shadow,
+    border: border(layer),
     keystrokeMargin: 30,
     item: {
       iconSpacing: 8,
       iconWidth: 14,
       padding: { left: 4, right: 4, top: 2, bottom: 2 },
       cornerRadius: 6,
-      label: text(theme, "sans", "primary", { size: "sm" }),
+      label: text(layer, "sans", { size: "sm" }),
       keystroke: {
-        ...text(theme, "sans", "muted", { size: "sm", weight: "bold" }),
+        ...text(layer, "sans", "base", "variant", { size: "sm", weight: "bold" }),
         padding: { left: 3, right: 3 },
       },
       hover: {
-        background: backgroundColor(theme, 300, "hovered"),
-        text: text(theme, "sans", "primary", { size: "sm" }),
+        background: background(layer, "base", "hovered"),
+        text: text(layer, "sans", "base", "hovered", { size: "sm" }),
       },
       active: {
-        background: backgroundColor(theme, 300, "active"),
-        text: text(theme, "sans", "active", { size: "sm" }),
+        background: background(layer, "base", "active"),
+        text: text(layer, "sans", "base", "active", { size: "sm" }),
       },
       activeHover: {
-        background: backgroundColor(theme, 300, "hovered"),
-        text: text(theme, "sans", "active", { size: "sm" }),
+        background: background(layer, "base", "active"),
+        text: text(layer, "sans", "base", "active", { size: "sm" }),
       },
     },
     separator: {
-      background: borderColor(theme, "primary"),
+      background: borderColor(layer),
       margin: { top: 2, bottom: 2 },
     },
   };

styles/src/styleTree/editor.ts πŸ”—

@@ -1,17 +1,18 @@
-import Theme from "../themes/common/theme";
+import { fontWeights } from "../common";
+import { ColorScheme, Elevation, Layer, StyleSets } from "../themes/common/colorScheme";
 import {
-  backgroundColor,
+  background,
   border,
   borderColor,
-  iconColor,
-  player,
-  popoverShadow,
+  foreground,
   text,
-  TextColor,
 } from "./components";
 import hoverPopover from "./hoverPopover";
 
-export default function editor(theme: Theme) {
+export default function editor(colorScheme: ColorScheme) {
+  let elevation = colorScheme.lowest;
+  let layer = elevation.top;
+
   const autocompleteItem = {
     cornerRadius: 6,
     padding: {
@@ -22,17 +23,17 @@ export default function editor(theme: Theme) {
     },
   };
 
-  function diagnostic(theme: Theme, color: TextColor) {
+  function diagnostic(layer: Layer, styleSet: StyleSets) {
     return {
       textScaleFactor: 0.857,
       header: {
-        border: border(theme, "primary", {
+        border: border(layer, {
           top: true,
         }),
       },
       message: {
-        text: text(theme, "sans", color, { size: "sm" }),
-        highlightText: text(theme, "sans", color, {
+        text: text(layer, "sans", styleSet, { size: "sm" }),
+        highlightText: text(layer, "sans", styleSet, {
           size: "sm",
           weight: "bold",
         }),
@@ -40,115 +41,193 @@ export default function editor(theme: Theme) {
     };
   }
 
-  const syntax: any = {};
-  for (const syntaxKey in theme.syntax) {
-    const style = theme.syntax[syntaxKey];
-    syntax[syntaxKey] = {
-      color: style.color,
-      weight: style.weight,
-      underline: style.underline,
-      italic: style.italic,
-    };
+  const syntax = {
+    primary: {
+      color: elevation.ramps.neutral(1).hex(),
+      weight: fontWeights.normal,
+    },
+    comment: {
+      color: elevation.ramps.neutral(0.71).hex(),
+      weight: fontWeights.normal,
+    },
+    punctuation: {
+      color: elevation.ramps.neutral(0.86).hex(),
+      weight: fontWeights.normal,
+    },
+    constant: {
+      color: elevation.ramps.neutral(0.57).hex(),
+      weight: fontWeights.normal,
+    },
+    keyword: {
+      color: elevation.ramps.blue(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    function: {
+      color: elevation.ramps.yellow(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    type: {
+      color: elevation.ramps.cyan(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    constructor: {
+      color: elevation.ramps.blue(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    variant: {
+      color: elevation.ramps.blue(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    property: {
+      color: elevation.ramps.blue(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    enum: {
+      color: elevation.ramps.orange(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    operator: {
+      color: elevation.ramps.orange(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    string: {
+      color: elevation.ramps.orange(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    number: {
+      color: elevation.ramps.green(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    boolean: {
+      color: elevation.ramps.green(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    predictive: {
+      color: elevation.ramps.neutral(0.57).hex(),
+      weight: fontWeights.normal,
+    },
+    title: {
+      color: elevation.ramps.yellow(0.5).hex(),
+      weight: fontWeights.bold,
+    },
+    emphasis: {
+      color: elevation.ramps.blue(0.5).hex(),
+      weight: fontWeights.normal,
+    },
+    "emphasis.strong": {
+      color: elevation.ramps.blue(0.5).hex(),
+      weight: fontWeights.bold,
+    },
+    linkUri: {
+      color: elevation.ramps.green(0.5).hex(),
+      weight: fontWeights.normal,
+      underline: true,
+    },
+    linkText: {
+      color: elevation.ramps.orange(0.5).hex(),
+      weight: fontWeights.normal,
+      italic: true,
+    },
   }
 
   return {
-    textColor: theme.syntax.primary.color,
-    background: backgroundColor(theme, 500),
-    activeLineBackground: theme.editor.line.active,
+    textColor: syntax.primary.color,
+    background: background(layer),
+    activeLineBackground: elevation.ramps.neutral(0.29).hex(),
+    highlightedLineBackground: elevation.ramps.neutral(0.18).hex(),
     codeActions: {
-      indicator: iconColor(theme, "secondary"),
+      indicator: foreground(layer, "base", "variant"),
       verticalScale: 0.618
     },
-    diffBackgroundDeleted: backgroundColor(theme, "error"),
-    diffBackgroundInserted: backgroundColor(theme, "ok"),
-    documentHighlightReadBackground: theme.editor.highlight.occurrence,
-    documentHighlightWriteBackground: theme.editor.highlight.activeOccurrence,
-    errorColor: theme.textColor.error,
-    gutterBackground: backgroundColor(theme, 500),
+    diffBackgroundDeleted: background(layer, "negative"),
+    diffBackgroundInserted: background(layer, "positive"),
+    documentHighlightReadBackground: elevation.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: This was blend
+    documentHighlightWriteBackground: elevation.ramps.neutral(0.5).alpha(0.4).hex(), // TODO: This was blend * 2
+    errorColor: foreground(layer, "negative"),
+    gutterBackground: background(layer),
     gutterPaddingFactor: 3.5,
-    highlightedLineBackground: theme.editor.line.highlighted,
-    lineNumber: theme.editor.gutter.primary,
-    lineNumberActive: theme.editor.gutter.active,
+    lineNumber: foreground(layer),
+    lineNumberActive: foreground(layer, "base", "active"),
     renameFade: 0.6,
     unnecessaryCodeFade: 0.5,
-    selection: player(theme, 1).selection,
+    selection: colorScheme.players[0],
     guestSelections: [
-      player(theme, 2).selection,
-      player(theme, 3).selection,
-      player(theme, 4).selection,
-      player(theme, 5).selection,
-      player(theme, 6).selection,
-      player(theme, 7).selection,
-      player(theme, 8).selection,
+      colorScheme.players[1],
+      colorScheme.players[2],
+      colorScheme.players[3],
+      colorScheme.players[4],
+      colorScheme.players[5],
+      colorScheme.players[6],
+      colorScheme.players[7],
     ],
     autocomplete: {
-      background: backgroundColor(theme, 500),
+      background: background(elevation.above.top),
       cornerRadius: 8,
       padding: 4,
-      border: border(theme, "secondary"),
-      shadow: popoverShadow(theme),
+      border: border(elevation.above.top),
+      shadow: elevation.above.shadow,
       item: autocompleteItem,
       hoveredItem: {
         ...autocompleteItem,
-        background: backgroundColor(theme, 500, "hovered"),
+        background: background(elevation.above.top, "base", "hovered"),
       },
       margin: {
         left: -14,
       },
-      matchHighlight: text(theme, "mono", "feature"),
+      matchHighlight: elevation.above.ramps.blue(0.5).hex(),
       selectedItem: {
         ...autocompleteItem,
-        background: backgroundColor(theme, 500, "active"),
+        background: background(elevation.above.top, "base", "active"),
       },
     },
     diagnosticHeader: {
-      background: backgroundColor(theme, 300),
+      background: background(elevation.middle),
       iconWidthFactor: 1.5,
       textScaleFactor: 0.857, // NateQ: Will we need dynamic sizing for text? If so let's create tokens for these.
-      border: border(theme, "secondary", {
+      border: border(elevation.middle, {
         bottom: true,
         top: true,
       }),
       code: {
-        ...text(theme, "mono", "secondary", { size: "sm" }),
+        ...text(elevation.middle, "mono", { size: "sm" }),
         margin: {
           left: 10,
         },
       },
       message: {
-        highlightText: text(theme, "sans", "primary", {
+        highlightText: text(elevation.middle, "sans", {
           size: "sm",
           weight: "bold",
         }),
-        text: text(theme, "sans", "secondary", { size: "sm" }),
+        text: text(elevation.middle, "sans", { size: "sm" }),
       },
     },
     diagnosticPathHeader: {
-      background: theme.editor.line.active,
+      background: background(elevation.middle),
       textScaleFactor: 0.857,
-      filename: text(theme, "mono", "primary", { size: "sm" }),
+      filename: text(elevation.middle, "mono", { size: "sm" }),
       path: {
-        ...text(theme, "mono", "muted", { size: "sm" }),
+        ...text(elevation.middle, "mono", { size: "sm" }),
         margin: {
           left: 12,
         },
       },
     },
-    errorDiagnostic: diagnostic(theme, "error"),
-    warningDiagnostic: diagnostic(theme, "warning"),
-    informationDiagnostic: diagnostic(theme, "info"),
-    hintDiagnostic: diagnostic(theme, "info"),
-    invalidErrorDiagnostic: diagnostic(theme, "secondary"),
-    invalidHintDiagnostic: diagnostic(theme, "secondary"),
-    invalidInformationDiagnostic: diagnostic(theme, "secondary"),
-    invalidWarningDiagnostic: diagnostic(theme, "secondary"),
-    hoverPopover: hoverPopover(theme),
+    errorDiagnostic: diagnostic(elevation.middle, "negative"),
+    warningDiagnostic: diagnostic(elevation.middle, "warning"),
+    informationDiagnostic: diagnostic(elevation.middle, "info"),
+    hintDiagnostic: diagnostic(elevation.middle, "positive"),
+    invalidErrorDiagnostic: diagnostic(elevation.middle, "base"),
+    invalidHintDiagnostic: diagnostic(elevation.middle, "base"),
+    invalidInformationDiagnostic: diagnostic(elevation.middle, "base"),
+    invalidWarningDiagnostic: diagnostic(elevation.middle, "base"),
+    hoverPopover: hoverPopover(elevation.above),
     linkDefinition: {
-      color: theme.syntax.linkUri.color,
-      underline: theme.syntax.linkUri.underline,
+      color: syntax.linkUri.color,
+      underline: syntax.linkUri.underline,
     },
     jumpIcon: {
-      color: iconColor(theme, "secondary"),
+      color: foreground(layer, "on"),
       iconWidth: 20,
       buttonWidth: 20,
       cornerRadius: 6,
@@ -159,14 +238,14 @@ export default function editor(theme: Theme) {
         right: 6,
       },
       hover: {
-        color: iconColor(theme, "active"),
-        background: backgroundColor(theme, "on500"),
+        color: foreground(layer, "on", "hovered"),
+        background: background(layer, "on", "hovered"),
       },
     },
     compositionMark: {
       underline: {
         thickness: 1.0,
-        color: borderColor(theme, "active")
+        color: borderColor(layer),
       },
     },
     syntax,

styles/src/styleTree/hoverPopover.ts πŸ”—

@@ -1,9 +1,10 @@
-import Theme from "../themes/common/theme";
-import { backgroundColor, border, popoverShadow, text } from "./components";
+import { Elevation } from "../themes/common/colorScheme";
+import { background, border, text } from "./components";
 
-export default function HoverPopover(theme: Theme) {
+export default function HoverPopover(elevation: Elevation) {
+  let layer = elevation.middle;
   let baseContainer = {
-    background: backgroundColor(theme, "on500"),
+    background: background(layer),
     cornerRadius: 8,
     padding: {
       left: 8,
@@ -11,8 +12,8 @@ export default function HoverPopover(theme: Theme) {
       top: 4,
       bottom: 4
     },
-    shadow: popoverShadow(theme),
-    border: border(theme, "secondary"),
+    shadow: elevation.shadow,
+    border: border(layer),
     margin: {
       left: -8,
     },
@@ -22,32 +23,23 @@ export default function HoverPopover(theme: Theme) {
     container: baseContainer,
     infoContainer: {
       ...baseContainer,
-      background: backgroundColor(theme, "on500Info"),
-      border: {
-        color: theme.ramps.blue(0).hex(),
-        width: 1,
-      },
+      background: background(layer, "info"),
+      border: border(layer, "info"),
     },
     warningContainer: {
       ...baseContainer,
-      background: backgroundColor(theme, "on500Warning"),
-      border: {
-        color: theme.ramps.yellow(0).hex(),
-        width: 1,
-      },
+      background: background(layer, "warning"),
+      border: border(layer, "warning"),
     },
     errorContainer: {
       ...baseContainer,
-      background: backgroundColor(theme, "on500Error"),
-      border: {
-        color: theme.ramps.red(0).hex(),
-        width: 1,
-      }
+      background: background(layer, "negative"),
+      border: border(layer, "negative"),
     },
     block_style: {
       padding: { top: 4 },
     },
-    prose: text(theme, "sans", "primary", { size: "sm" }),
-    highlight: theme.editor.highlight.occurrence,
+    prose: text(layer, "sans", { size: "sm" }),
+    highlight: elevation.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: blend was used here. Replace with something better
   };
 }

styles/src/styleTree/picker.ts πŸ”—

@@ -1,15 +1,15 @@
-import Theme from "../themes/common/theme";
+import { ColorScheme } from "../themes/common/colorScheme";
 import {
-  backgroundColor,
+  background,
   border,
-  player,
-  modalShadow,
   text,
 } from "./components";
 
-export default function picker(theme: Theme) {
+export default function picker(colorScheme: ColorScheme) {
+  let elevation = colorScheme.highest;
+  let layer = elevation.middle;
   return {
-    background: backgroundColor(theme, 300),
+    background: background(layer),
     cornerRadius: 8,
     padding: 8,
     item: {
@@ -20,19 +20,19 @@ export default function picker(theme: Theme) {
         top: 4,
       },
       cornerRadius: 8,
-      text: text(theme, "sans", "secondary"),
-      highlightText: text(theme, "sans", "feature", { weight: "bold" }),
+      text: text(layer, "sans"),
+      highlightText: text(layer, "sans", { weight: "bold" }),
       active: {
-        background: backgroundColor(theme, 300, "active"),
-        text: text(theme, "sans", "active"),
+        background: background(layer, "base", "active"),
+        text: text(layer, "sans", "base", "active"),
       },
       hover: {
-        background: backgroundColor(theme, 300, "hovered"),
+        background: background(layer, "base", "hovered"),
       },
     },
-    border: border(theme, "primary"),
+    border: border(layer),
     empty: {
-      text: text(theme, "sans", "muted"),
+      text: text(layer, "sans"),
       padding: {
         bottom: 4,
         left: 12,
@@ -41,12 +41,12 @@ export default function picker(theme: Theme) {
       },
     },
     inputEditor: {
-      background: backgroundColor(theme, 500),
+      background: background(layer, "on"),
       cornerRadius: 8,
-      placeholderText: text(theme, "sans", "placeholder"),
-      selection: player(theme, 1).selection,
-      text: text(theme, "mono", "primary"),
-      border: border(theme, "secondary"),
+      placeholderText: text(layer, "sans", "on", "disabled"),
+      selection: colorScheme.players[0],
+      text: text(layer, "mono", "on"),
+      border: border(layer, "on"),
       padding: {
         bottom: 7,
         left: 16,
@@ -54,6 +54,6 @@ export default function picker(theme: Theme) {
         top: 7,
       },
     },
-    shadow: modalShadow(theme),
+    shadow: elevation.shadow,
   };
 }

styles/src/styleTree/projectDiagnostics.ts πŸ”—

@@ -1,12 +1,13 @@
-import Theme from "../themes/common/theme";
-import { backgroundColor, text } from "./components";
+import { ColorScheme } from "../themes/common/colorScheme";
+import { background, text } from "./components";
 
-export default function projectDiagnostics(theme: Theme) {
+export default function projectDiagnostics(colorScheme: ColorScheme) {
+  let layer = colorScheme.lowest.top;
   return {
-    background: backgroundColor(theme, 500),
+    background: background(layer),
     tabIconSpacing: 4,
     tabIconWidth: 13,
     tabSummarySpacing: 10,
-    emptyMessage: text(theme, "sans", "secondary", { size: "md" }),
+    emptyMessage: text(layer, "sans", "base", "variant", { size: "md" }),
   };
 }

styles/src/styleTree/projectPanel.ts πŸ”—

@@ -1,36 +1,37 @@
-import Theme from "../themes/common/theme";
+import { ColorScheme } from "../themes/common/colorScheme";
 import { panel } from "./app";
-import { backgroundColor, iconColor, player, text } from "./components";
+import { background, foreground, text } from "./components";
 
-export default function projectPanel(theme: Theme) {
+export default function projectPanel(colorScheme: ColorScheme) {
+  let layer = colorScheme.lowest.middle;
   return {
     ...panel,
     padding: { left: 12, right: 12, top: 6, bottom: 6 },
     indentWidth: 8,
     entry: {
       height: 24,
-      iconColor: iconColor(theme, "muted"),
+      iconColor: foreground(layer, "on"),
       iconSize: 8,
       iconSpacing: 8,
-      text: text(theme, "mono", "secondary", { size: "sm" }),
+      text: text(layer, "mono", "on", { size: "sm" }),
       hover: {
-        background: backgroundColor(theme, 300, "hovered"),
+        background: background(layer, "on", "hovered"),
       },
       active: {
-        background: backgroundColor(theme, 300, "active"),
-        text: text(theme, "mono", "active", { size: "sm" }),
+        background: background(layer, "base", "active"),
+        text: text(layer, "mono", "base", "active", { size: "sm" }),
       },
       activeHover: {
-        background: backgroundColor(theme, 300, "active"),
-        text: text(theme, "mono", "active", { size: "sm" }),
+        background: background(layer, "base", "hovered"),
+        text: text(layer, "mono", "base", "active", { size: "sm" }),
       },
     },
     cutEntryFade: 0.4,
     ignoredEntryFade: 0.6,
     filenameEditor: {
-      background: backgroundColor(theme, "on300"),
-      text: text(theme, "mono", "active", { size: "sm" }),
-      selection: player(theme, 1).selection,
+      background: background(layer, "on"),
+      text: text(layer, "mono", "on", "active", { size: "sm" }),
+      selection: colorScheme.players[0],
     },
   };
 }

styles/src/styleTree/search.ts πŸ”—

@@ -1,17 +1,19 @@
-import Theme from "../themes/common/theme";
-import { backgroundColor, border, player, text } from "./components";
+import { ColorScheme } from "../themes/common/colorScheme";
+import { background, border, text } from "./components";
+
+export default function search(colorScheme: ColorScheme) {
+  let layer = colorScheme.lowest.top;
 
-export default function search(theme: Theme) {
   // Search input
   const editor = {
-    background: backgroundColor(theme, 500),
+    background: background(layer),
     cornerRadius: 8,
     minWidth: 200,
     maxWidth: 500,
-    placeholderText: text(theme, "mono", "placeholder"),
-    selection: player(theme, 1).selection,
-    text: text(theme, "mono", "active"),
-    border: border(theme, "secondary"),
+    placeholderText: text(layer, "mono", "base", "disabled"),
+    selection: colorScheme.players[0],
+    text: text(layer, "mono", "base", "active"),
+    border: border(layer),
     margin: {
       right: 12,
     },
@@ -24,14 +26,14 @@ export default function search(theme: Theme) {
   };
 
   return {
-    matchBackground: theme.editor.highlight.match,
+    matchBackground: background(layer), // theme.editor.highlight.match,
     tabIconSpacing: 8,
     tabIconWidth: 14,
     optionButton: {
-      ...text(theme, "mono", "secondary"),
-      background: backgroundColor(theme, "on500"),
+      ...text(layer, "mono", "on"),
+      background: background(layer, "on"),
       cornerRadius: 6,
-      border: border(theme, "secondary"),
+      border: border(layer, "on"),
       margin: {
         right: 4,
       },
@@ -42,28 +44,28 @@ export default function search(theme: Theme) {
         top: 2,
       },
       active: {
-        ...text(theme, "mono", "active"),
-        background: backgroundColor(theme, "on500", "active"),
-        border: border(theme, "muted"),
+        ...text(layer, "mono", "on", "active"),
+        background: background(layer, "on", "active"),
+        border: border(layer, "on", "active"),
       },
       clicked: {
-        ...text(theme, "mono", "active"),
-        background: backgroundColor(theme, "on300", "active"),
-        border: border(theme, "secondary"),
+        ...text(layer, "mono", "on", "pressed"),
+        background: background(layer, "on", "pressed"),
+        border: border(layer, "on", "pressed"),
       },
       hover: {
-        ...text(theme, "mono", "active"),
-        background: backgroundColor(theme, "on500", "hovered"),
-        border: border(theme, "muted"),
+        ...text(layer, "mono", "on", "hovered"),
+        background: background(layer, "on", "hovered"),
+        border: border(layer, "on", "hovered"),
       },
     },
     editor,
     invalidEditor: {
       ...editor,
-      border: border(theme, "error"),
+      border: border(layer, "negative"),
     },
     matchIndex: {
-      ...text(theme, "mono", "muted"),
+      ...text(layer, "mono", "on", "variant"),
       padding: 6,
     },
     optionButtonGroup: {
@@ -73,7 +75,7 @@ export default function search(theme: Theme) {
       },
     },
     resultsStatus: {
-      ...text(theme, "mono", "primary"),
+      ...text(layer, "mono", "on"),
       size: 18,
     },
   };

styles/src/styleTree/statusBar.ts πŸ”—

@@ -1,8 +1,9 @@
-import Theme from "../themes/common/theme";
-import { backgroundColor, border, iconColor, text } from "./components";
-import { workspaceBackground } from "./workspace";
+import { ColorScheme } from "../themes/common/colorScheme";
+import { background, border, foreground, text } from "./components";
+
+export default function statusBar(colorScheme: ColorScheme) {
+  let layer = colorScheme.lowest.bottom;
 
-export default function statusBar(theme: Theme) {
   const statusContainer = {
     cornerRadius: 6,
     padding: { top: 3, bottom: 3, left: 6, right: 6 },
@@ -22,70 +23,70 @@ export default function statusBar(theme: Theme) {
       left: 6,
       right: 6,
     },
-    border: border(theme, "primary", { top: true, overlay: true }),
-    cursorPosition: text(theme, "sans", "secondary"),
-    autoUpdateProgressMessage: text(theme, "sans", "secondary"),
-    autoUpdateDoneMessage: text(theme, "sans", "secondary"),
+    border: border(layer, { top: true, overlay: true }),
+    cursorPosition: text(layer, "sans"),
+    autoUpdateProgressMessage: text(layer, "sans"),
+    autoUpdateDoneMessage: text(layer, "sans"),
     lspStatus: {
       ...diagnosticStatusContainer,
       iconSpacing: 4,
       iconWidth: 14,
       height: 18,
-      message: text(theme, "sans", "secondary"),
-      iconColor: iconColor(theme, "muted"),
+      message: text(layer, "sans"),
+      iconColor: foreground(layer),
       hover: {
-        message: text(theme, "sans", "primary"),
-        iconColor: iconColor(theme, "primary"),
-        background: backgroundColor(theme, 300, "hovered"),
+        message: text(layer, "sans"),
+        iconColor: foreground(layer),
+        background: background(layer),
       },
     },
     diagnosticMessage: {
-      ...text(theme, "sans", "secondary"),
-      hover: text(theme, "sans", "active"),
+      ...text(layer, "sans"),
+      hover: text(layer, "sans", "base", "hovered"),
     },
     feedback: {
-      ...text(theme, "sans", "secondary"),
-      hover: text(theme, "sans", "active"),
+      ...text(layer, "sans"),
+      hover: text(layer, "sans"),
     },
     diagnosticSummary: {
       height: 16,
       iconWidth: 16,
       iconSpacing: 2,
       summarySpacing: 6,
-      text: text(theme, "sans", "primary", { size: "sm" }),
-      iconColorOk: iconColor(theme, "muted"),
-      iconColorWarning: iconColor(theme, "warning"),
-      iconColorError: iconColor(theme, "error"),
+      text: text(layer, "sans", { size: "sm" }),
+      iconColorOk: foreground(layer, "positive"),
+      iconColorWarning: foreground(layer, "warning"),
+      iconColorError: foreground(layer, "negative"),
       containerOk: {
         cornerRadius: 6,
         padding: { top: 3, bottom: 3, left: 7, right: 7 },
       },
       containerWarning: {
         ...diagnosticStatusContainer,
-        background: backgroundColor(theme, "warning"),
-        border: border(theme, "warning"),
+        background: background(layer, "warning"),
+        border: border(layer, "warning"),
       },
       containerError: {
         ...diagnosticStatusContainer,
-        background: backgroundColor(theme, "error"),
-        border: border(theme, "error"),
+        background: background(layer, "negative"),
+        border: border(layer, "negative"),
       },
       hover: {
-        iconColorOk: iconColor(theme, "active"),
+        iconColorOk: foreground(layer, "on"),
         containerOk: {
           cornerRadius: 6,
           padding: { top: 3, bottom: 3, left: 7, right: 7 },
-          background: backgroundColor(theme, 300, "hovered"),
+          background: background(layer, "on", "hovered"),
         },
         containerWarning: {
           ...diagnosticStatusContainer,
-          background: backgroundColor(theme, "warning", "hovered"),
-          border: border(theme, "warning"),
+          background: background(layer, "warning", "hovered"),
+          border: border(layer, "warning", "hovered"),
         },
         containerError: {
           ...diagnosticStatusContainer,
-          background: backgroundColor(theme, "error", "hovered"),
-          border: border(theme, "error"),
+          background: background(layer, "negative", "hovered"),
+          border: border(layer, "negative", "hovered"),
         },
       },
     },
@@ -95,22 +96,22 @@ export default function statusBar(theme: Theme) {
       item: {
         ...statusContainer,
         iconSize: 16,
-        iconColor: iconColor(theme, "muted"),
+        iconColor: foreground(layer),
         hover: {
-          iconColor: iconColor(theme, "active"),
-          background: backgroundColor(theme, 300, "hovered"),
+          iconColor: foreground(layer, "base", "hovered"),
+          background: background(layer, "base", "hovered"),
         },
         active: {
-          iconColor: iconColor(theme, "active"),
-          background: backgroundColor(theme, 300, "active"),
+          iconColor: foreground(layer, "base", "active"),
+          background: background(layer, "base", "active"),
         },
       },
       badge: {
         cornerRadius: 3,
         padding: 2,
         margin: { bottom: -1, right: -1 },
-        border: { width: 1, color: workspaceBackground(theme) },
-        background: iconColor(theme, "feature"),
+        border: border(layer),
+        background: background(layer),
       },
     },
   };

styles/src/styleTree/tabBar.ts πŸ”—

@@ -1,39 +1,42 @@
-import Theme from "../themes/common/theme";
+import { ColorScheme } from "../themes/common/colorScheme";
 import { withOpacity } from "../utils/color";
-import { iconColor, text, border, backgroundColor, draggedShadow } from "./components";
+import { text, border, background, foreground } from "./components";
 
-export default function tabBar(theme: Theme) {
+export default function tabBar(colorScheme: ColorScheme) {
   const height = 32;
 
+  let elevation = colorScheme.lowest;
+  let layer = elevation.middle;
+
   const tab = {
     height,
-    background: backgroundColor(theme, 300),
-    border: border(theme, "primary", {
+    background: background(layer),
+    border: border(layer, {
       left: true,
       bottom: true,
       overlay: true,
     }),
-    iconClose: iconColor(theme, "muted"),
-    iconCloseActive: iconColor(theme, "active"),
-    iconConflict: iconColor(theme, "warning"),
-    iconDirty: iconColor(theme, "info"),
+    iconClose: foreground(layer),
+    iconCloseActive: foreground(layer, "base", "active"),
+    iconConflict: foreground(layer, "warning"),
+    iconDirty: foreground(layer, "info"),
     iconWidth: 8,
     spacing: 8,
-    text: text(theme, "sans", "secondary", { size: "sm" }),
+    text: text(layer, "sans", { size: "sm" }),
     padding: {
       left: 8,
       right: 8,
     },
     description: {
       margin: { left: 6, top: 1 },
-      ...text(theme, "sans", "muted", { size: "2xs" })
+      ...text(layer, "sans", "base", "variant", { size: "2xs" })
     }
   };
 
   const activePaneActiveTab = {
     ...tab,
-    background: backgroundColor(theme, 500),
-    text: text(theme, "sans", "active", { size: "sm" }),
+    background: background(elevation.top),
+    text: text(elevation.top, "sans", "base", "active", { size: "sm" }),
     border: {
       ...tab.border,
       bottom: false
@@ -42,14 +45,14 @@ export default function tabBar(theme: Theme) {
 
   const inactivePaneInactiveTab = {
     ...tab,
-    background: backgroundColor(theme, 300),
-    text: text(theme, "sans", "muted", { size: "sm" }),
+    background: background(layer),
+    text: text(layer, "sans", { size: "sm" }),
   };
 
   const inactivePaneActiveTab = {
     ...tab,
-    background: backgroundColor(theme, 500),
-    text: text(theme, "sans", "secondary", { size: "sm" }),
+    background: background(layer),
+    text: text(layer, "sans", "base", "active", { size: "sm" }),
     border: {
       ...tab.border,
       bottom: false
@@ -59,15 +62,16 @@ export default function tabBar(theme: Theme) {
   const draggedTab = {
     ...activePaneActiveTab,
     background: withOpacity(tab.background, 0.8),
-    border: undefined as any, // Remove border
-    shadow: draggedShadow(theme),
+    border: undefined as any,
+    shadow: elevation.above.shadow,
   }
 
   return {
     height,
-    background: backgroundColor(theme, 300),
-    dropTargetOverlayColor: withOpacity(theme.textColor.muted, 0.6),
-    border: border(theme, "primary", {
+    background: background(layer),
+    dropTargetOverlayColor: withOpacity(foreground(layer), 0.6),
+    border: border(layer, {
+      left: true,
       bottom: true,
       overlay: true,
     }),
@@ -81,11 +85,11 @@ export default function tabBar(theme: Theme) {
     },
     draggedTab,
     paneButton: {
-      color: iconColor(theme, "secondary"),
+      color: foreground(layer),
       iconWidth: 12,
       buttonWidth: activePaneActiveTab.height,
       hover: {
-        color: iconColor(theme, "active"),
+        color: foreground(layer, "base", "hovered"),
       },
     },
     paneButtonContainer: {

styles/src/styleTree/terminal.ts πŸ”—

@@ -1,7 +1,6 @@
-import Theme from "../themes/common/theme";
-import { border, modalShadow, player } from "./components";
+import { Elevation } from "../themes/common/colorScheme";
 
-export default function terminal(theme: Theme) {
+export default function terminal(elevation: Elevation) {
   /**
   * Colors are controlled per-cell in the terminal grid. 
   * Cells can be set to any of these more 'theme-capable' colors
@@ -9,57 +8,45 @@ export default function terminal(theme: Theme) {
   * Here are the common interpretations of these names:
   * https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
   */
-  let colors = {
-    black: theme.ramps.neutral(0).hex(),
-    red: theme.ramps.red(0.5).hex(),
-    green: theme.ramps.green(0.5).hex(),
-    yellow: theme.ramps.yellow(0.5).hex(),
-    blue: theme.ramps.blue(0.5).hex(),
-    magenta: theme.ramps.magenta(0.5).hex(),
-    cyan: theme.ramps.cyan(0.5).hex(),
-    white: theme.ramps.neutral(7).hex(),
-    brightBlack: theme.ramps.neutral(4).hex(),
-    brightRed: theme.ramps.red(0.25).hex(),
-    brightGreen: theme.ramps.green(0.25).hex(),
-    brightYellow: theme.ramps.yellow(0.25).hex(),
-    brightBlue: theme.ramps.blue(0.25).hex(),
-    brightMagenta: theme.ramps.magenta(0.25).hex(),
-    brightCyan: theme.ramps.cyan(0.25).hex(),
-    brightWhite: theme.ramps.neutral(7).hex(),
+  return {
+    black: elevation.ramps.neutral(0).hex(),
+    red: elevation.ramps.red(0.5).hex(),
+    green: elevation.ramps.green(0.5).hex(),
+    yellow: elevation.ramps.yellow(0.5).hex(),
+    blue: elevation.ramps.blue(0.5).hex(),
+    magenta: elevation.ramps.magenta(0.5).hex(),
+    cyan: elevation.ramps.cyan(0.5).hex(),
+    white: elevation.ramps.neutral(1).hex(),
+    brightBlack: elevation.ramps.neutral(0.4).hex(),
+    brightRed: elevation.ramps.red(0.25).hex(),
+    brightGreen: elevation.ramps.green(0.25).hex(),
+    brightYellow: elevation.ramps.yellow(0.25).hex(),
+    brightBlue: elevation.ramps.blue(0.25).hex(),
+    brightMagenta: elevation.ramps.magenta(0.25).hex(),
+    brightCyan: elevation.ramps.cyan(0.25).hex(),
+    brightWhite: elevation.ramps.neutral(1).hex(),
     /**
      * Default color for characters
      */
-    foreground: theme.ramps.neutral(7).hex(),
+    foreground: elevation.ramps.neutral(1).hex(),
     /**
      * Default color for the rectangle background of a cell
      */
-    background: theme.ramps.neutral(0).hex(),
-    modalBackground: theme.ramps.neutral(1).hex(),
+    background: elevation.ramps.neutral(0).hex(),
+    modalBackground: elevation.ramps.neutral(0.1).hex(),
     /**
      * Default color for the cursor
      */
-    cursor: player(theme, 1).selection.cursor,
-    dimBlack: theme.ramps.neutral(7).hex(),
-    dimRed: theme.ramps.red(0.75).hex(),
-    dimGreen: theme.ramps.green(0.75).hex(),
-    dimYellow: theme.ramps.yellow(0.75).hex(),
-    dimBlue: theme.ramps.blue(0.75).hex(),
-    dimMagenta: theme.ramps.magenta(0.75).hex(),
-    dimCyan: theme.ramps.cyan(0.75).hex(),
-    dimWhite: theme.ramps.neutral(5).hex(),
-    brightForeground: theme.ramps.neutral(7).hex(),
-    dimForeground: theme.ramps.neutral(0).hex(),
-  };
-
-  return {
-    colors,
-    modalContainer: {
-      background: colors.modalBackground,
-      cornerRadius: 8,
-      padding: 8,
-      margin: 25,
-      border: border(theme, "primary"),
-      shadow: modalShadow(theme),
-    },
+    cursor: elevation.ramps.blue(0).hex(),
+    dimBlack: elevation.ramps.neutral(1).hex(),
+    dimRed: elevation.ramps.red(0.75).hex(),
+    dimGreen: elevation.ramps.green(0.75).hex(),
+    dimYellow: elevation.ramps.yellow(0.75).hex(),
+    dimBlue: elevation.ramps.blue(0.75).hex(),
+    dimMagenta: elevation.ramps.magenta(0.75).hex(),
+    dimCyan: elevation.ramps.cyan(0.75).hex(),
+    dimWhite: elevation.ramps.neutral(0.6).hex(),
+    brightForeground: elevation.ramps.neutral(1).hex(),
+    dimForeground: elevation.ramps.neutral(0).hex(),
   };
 }

styles/src/styleTree/tooltip.ts πŸ”—

@@ -1,21 +1,23 @@
-import Theme from "../themes/common/theme";
-import { backgroundColor, border, popoverShadow, text } from "./components";
+import { ColorScheme } from "../themes/common/colorScheme";
+import { background, border, text } from "./components";
 
-export default function tooltip(theme: Theme) {
+export default function tooltip(colorScheme: ColorScheme) {
+  let elevation = colorScheme.middle;
+  let layer = colorScheme.middle.middle;
   return {
-    background: backgroundColor(theme, 500),
-    border: border(theme, "secondary"),
+    background: background(layer),
+    border: border(layer),
     padding: { top: 4, bottom: 4, left: 8, right: 8 },
     margin: { top: 6, left: 6 },
-    shadow: popoverShadow(theme),
+    shadow: elevation.shadow,
     cornerRadius: 6,
-    text: text(theme, "sans", "primary", { size: "xs" }),
+    text: text(layer, "sans", { size: "xs" }),
     keystroke: {
-      background: backgroundColor(theme, "on500"),
+      background: background(layer, "on"),
       cornerRadius: 4,
       margin: { left: 6 },
       padding: { left: 4, right: 4 },
-      ...text(theme, "mono", "secondary", { size: "xs", weight: "bold" }),
+      ...text(layer, "mono", "on", { size: "xs", weight: "bold" }),
     },
     maxTextWidth: 200,
   };

styles/src/styleTree/updateNotification.ts πŸ”—

@@ -1,29 +1,30 @@
-import Theme from "../themes/common/theme";
-import { iconColor, text } from "./components";
+import { ColorScheme } from "../themes/common/colorScheme";
+import { foreground, text } from "./components";
 
 const headerPadding = 8;
 
-export default function updateNotification(theme: Theme): Object {
+export default function updateNotification(colorScheme: ColorScheme): Object {
+  let layer = colorScheme.middle.middle;
   return {
     message: {
-      ...text(theme, "sans", "primary", { size: "xs" }),
+      ...text(layer, "sans", { size: "xs" }),
       margin: { left: headerPadding, right: headerPadding },
     },
     actionMessage: {
-      ...text(theme, "sans", "secondary", { size: "xs" }),
+      ...text(layer, "sans", { size: "xs" }),
       margin: { left: headerPadding, top: 6, bottom: 6 },
       hover: {
-        color: theme.textColor["active"],
+        color: foreground(layer, "base", "hovered"),
       },
     },
     dismissButton: {
-      color: iconColor(theme, "secondary"),
+      color: foreground(layer),
       iconWidth: 8,
       iconHeight: 8,
       buttonWidth: 8,
       buttonHeight: 8,
       hover: {
-        color: iconColor(theme, "primary"),
+        color: foreground(layer, "base", "hovered"),
       },
     },
   };

styles/src/styleTree/workspace.ts πŸ”—

@@ -1,35 +1,33 @@
-import Theme from "../themes/common/theme";
+import { ColorScheme } from "../themes/common/colorScheme";
 import { withOpacity } from "../utils/color";
 import {
-  backgroundColor,
+  background,
   border,
-  iconColor,
-  modalShadow,
-  text,
+  borderColor,
+  foreground,
+  text
 } from "./components";
 import statusBar from "./statusBar";
 import tabBar from "./tabBar";
 
-export function workspaceBackground(theme: Theme) {
-  return backgroundColor(theme, 300);
-}
-
-export default function workspace(theme: Theme) {
+export default function workspace(colorScheme: ColorScheme) {
+  const elevation = colorScheme.lowest;
+  const layer = elevation.middle;
   const titlebarPadding = 6;
 
   return {
-    background: backgroundColor(theme, 300),
+    background: background(layer),
     joiningProjectAvatar: {
       cornerRadius: 40,
       width: 80,
     },
     joiningProjectMessage: {
       padding: 12,
-      ...text(theme, "sans", "primary", { size: "lg" }),
+      ...text(layer, "sans", { size: "lg" }),
     },
     leaderBorderOpacity: 0.7,
     leaderBorderWidth: 2.0,
-    tabBar: tabBar(theme),
+    tabBar: tabBar(colorScheme),
     modal: {
       margin: {
         bottom: 52,
@@ -39,28 +37,26 @@ export default function workspace(theme: Theme) {
     },
     sidebar: {
       initialSize: 240,
-      border: {
-        color: border(theme, "primary").color,
-        width: 1,
-        left: true,
-        right: true,
-      }
+      border: border(
+        layer,
+        { left: true, right: true }
+      ),
     },
     paneDivider: {
-      color: border(theme, "secondary").color,
+      color: borderColor(layer),
       width: 1,
     },
-    statusBar: statusBar(theme),
+    statusBar: statusBar(colorScheme),
     titlebar: {
       avatarWidth: 18,
       avatarMargin: 8,
       height: 33,
-      background: backgroundColor(theme, 100),
+      background: background(layer),
       padding: {
         left: 80,
         right: titlebarPadding,
       },
-      title: text(theme, "sans", "primary"),
+      title: text(layer, "sans"),
       avatar: {
         cornerRadius: 10,
         border: {
@@ -74,10 +70,10 @@ export default function workspace(theme: Theme) {
         // TODO: The background for this ideally should be
         // set with a token, not hardcoded in rust
       },
-      border: border(theme, "primary", { bottom: true, overlay: true }),
+      border: border(layer, { bottom: true, overlay: true }),
       signInPrompt: {
-        background: backgroundColor(theme, 100),
-        border: border(theme, "secondary"),
+        background: background(layer),
+        border: border(layer),
         cornerRadius: 6,
         margin: {
           top: 1,
@@ -88,15 +84,15 @@ export default function workspace(theme: Theme) {
           left: 7,
           right: 7,
         },
-        ...text(theme, "sans", "secondary", { size: "xs" }),
+        ...text(layer, "sans", { size: "xs" }),
         hover: {
-          ...text(theme, "sans", "active", { size: "xs" }),
-          background: backgroundColor(theme, "on300", "hovered"),
-          border: border(theme, "primary"),
+          ...text(layer, "sans", "on", "hovered", { size: "xs" }),
+          background: background(layer, "on", "hovered"),
+          border: border(layer, "on", "hovered"),
         },
       },
       offlineIcon: {
-        color: iconColor(theme, "secondary"),
+        color: foreground(layer, "on"),
         width: 16,
         margin: {
           left: titlebarPadding,
@@ -106,9 +102,9 @@ export default function workspace(theme: Theme) {
         },
       },
       outdatedWarning: {
-        ...text(theme, "sans", "warning", { size: "xs" }),
-        background: backgroundColor(theme, "warning"),
-        border: border(theme, "warning"),
+        ...text(layer, "sans", "warning", { size: "xs" }),
+        background: background(layer, "warning"),
+        border: border(layer, "warning"),
         margin: {
           left: titlebarPadding,
         },
@@ -121,39 +117,39 @@ export default function workspace(theme: Theme) {
     },
     toolbar: {
       height: 34,
-      background: backgroundColor(theme, 500),
-      border: border(theme, "secondary", { bottom: true }),
+      background: background(layer),
+      border: border(layer, "base", "variant", { bottom: true }),
       itemSpacing: 8,
       navButton: {
-        color: iconColor(theme, "primary"),
+        color: foreground(layer, "on"),
         iconWidth: 12,
         buttonWidth: 24,
         cornerRadius: 6,
         hover: {
-          color: iconColor(theme, "active"),
-          background: backgroundColor(theme, "on500", "hovered"),
+          color: foreground(layer, "on", "hovered"),
+          background: background(layer, "on", "hovered"),
         },
         disabled: {
-          color: withOpacity(iconColor(theme, "muted"), 0.6),
+          color: foreground(layer, "on", "disabled"),
         },
       },
       padding: { left: 8, right: 8, top: 4, bottom: 4 },
     },
     breadcrumbs: {
-      ...text(theme, "mono", "secondary"),
+      ...text(layer, "mono", "on", "variant"),
       padding: { left: 6 },
     },
     disconnectedOverlay: {
-      ...text(theme, "sans", "active"),
-      background: withOpacity(theme.backgroundColor[500].base, 0.8),
+      ...text(layer, "sans"),
+      background: withOpacity(background(layer), 0.8),
     },
     notification: {
       margin: { top: 10 },
-      background: backgroundColor(theme, 300),
+      background: background(elevation.above.middle),
       cornerRadius: 6,
       padding: 12,
-      border: border(theme, "primary"),
-      shadow: modalShadow(theme),
+      border: border(elevation.above.middle),
+      shadow: elevation.above.shadow,
     },
     notifications: {
       width: 400,
@@ -162,17 +158,14 @@ export default function workspace(theme: Theme) {
     dock: {
       initialSizeRight: 640,
       initialSizeBottom: 480,
-      wash_color: withOpacity(theme.backgroundColor[500].base, 0.5),
+      wash_color: withOpacity(background(elevation.top), 0.5),
       panel: {
-        border: {
-          ...border(theme, "secondary"),
-          width: 1
-        },
+        border: border(elevation.top),
       },
       maximized: {
-        margin: 24,
-        border: border(theme, "secondary", { "overlay": true }),
-        shadow: modalShadow(theme),
+        margin: 32,
+        border: border(elevation.above.top, { "overlay": true }),
+        shadow: elevation.above.shadow,
       }
     }
   };

styles/src/themes.ts πŸ”—

@@ -1,31 +0,0 @@
-import fs from "fs";
-import path from "path";
-import Theme from "./themes/common/theme";
-
-const themes: Theme[] = [];
-export default themes;
-
-const internalThemes: Theme[] = [];
-export { internalThemes }
-
-const experimentalThemes: Theme[] = [];
-export { experimentalThemes }
-
-
-function fillThemes(themesPath: string, themes: Theme[]) {
-  for (const fileName of fs.readdirSync(themesPath)) {
-    if (fileName == "template.ts") continue;
-    const filePath = path.join(themesPath, fileName);
-
-    if (fs.statSync(filePath).isFile()) {
-      const theme = require(filePath);
-      if (theme.dark) themes.push(theme.dark);
-      if (theme.light) themes.push(theme.light);
-    }
-  }
-}
-
-fillThemes(path.resolve(`${__dirname}/themes`), themes)
-fillThemes(path.resolve(`${__dirname}/themes/internal`), internalThemes)
-fillThemes(path.resolve(`${__dirname}/themes/experiments`), experimentalThemes)
-

styles/src/themes/abruzzo.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "abruzzo";
 
@@ -24,5 +24,5 @@ const ramps = {
   magenta: colorRamp(chroma("#c1a3ef")),
 };
 
-export const dark = createTheme(`${name}`, false, ramps);
+export const dark = createColorScheme(`${name}`, false, ramps);
 // export const light = createTheme(`${name}-light`, true, ramps);

styles/src/themes/andromeda.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "andromeda";
 
@@ -24,4 +24,4 @@ const ramps = {
   magenta: colorRamp(chroma("#C74DED")),
 };
 
-export const dark = createTheme(`${name}`, false, ramps);
+export const dark = createColorScheme(`${name}`, false, ramps);

styles/src/themes/brushtrees.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "brush-tree";
 
@@ -24,5 +24,5 @@ const ramps = {
   magenta: colorRamp(chroma("#b39f9f")),
 };
 
-export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const dark = createColorScheme(`${name}-dark`, false, ramps);
+export const light = createColorScheme(`${name}-light`, true, ramps);

styles/src/themes/cave.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "cave";
 
@@ -24,5 +24,5 @@ const ramps = {
   magenta: colorRamp(chroma("#bf40bf")),
 };
 
-export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const dark = createColorScheme(`${name}-dark`, false, ramps);
+export const light = createColorScheme(`${name}-light`, true, ramps);

styles/src/themes/common/base16.ts πŸ”—

@@ -1,288 +0,0 @@
-import chroma, { Color, Scale } from "chroma-js";
-import { fontWeights } from "../../common";
-import { withOpacity } from "../../utils/color";
-import Theme, { buildPlayer, Syntax } from "./theme";
-
-export function colorRamp(color: Color): Scale {
-  let hue = color.hsl()[0];
-  let endColor = chroma.hsl(hue, 0.88, 0.96);
-  let startColor = chroma.hsl(hue, 0.68, 0.12);
-  return chroma.scale([startColor, color, endColor]).mode("hsl");
-}
-
-export function createTheme(
-  name: string,
-  isLight: boolean,
-  color_ramps: { [rampName: string]: Scale }
-): Theme {
-  let ramps: typeof color_ramps = {};
-  // Chromajs mutates the underlying ramp when you call domain. This causes problems because
-  // we now store the ramps object in the theme so that we can pull colors out of them.
-  // So instead of calling domain and storing the result, we have to construct new ramps for each
-  // theme so that we don't modify the passed in ramps.
-  // This combined with an error in the type definitions for chroma js means we have to cast the colors
-  // function to any in order to get the colors back out from the original ramps.
-  if (isLight) {
-    for (var rampName in color_ramps) {
-      ramps[rampName] = chroma
-        .scale((color_ramps[rampName].colors as any)())
-        .domain([1, 0]);
-    }
-    ramps.neutral = chroma
-      .scale((color_ramps.neutral.colors as any)())
-      .domain([7, 0]);
-  } else {
-    for (var rampName in color_ramps) {
-      ramps[rampName] = chroma
-        .scale((color_ramps[rampName].colors as any)())
-        .domain([0, 1]);
-    }
-    ramps.neutral = chroma
-      .scale((color_ramps.neutral.colors as any)())
-      .domain([0, 7]);
-  }
-
-  let blend = isLight ? 0.12 : 0.24;
-
-  function sample(ramp: Scale, index: number): string {
-    return ramp(index).hex();
-  }
-  const darkest = ramps.neutral(isLight ? 7 : 0).hex();
-
-  const backgroundColor = {
-    // Title bar
-    100: {
-      base: sample(ramps.neutral, 1.25),
-      hovered: sample(ramps.neutral, 1.5),
-      active: sample(ramps.neutral, 1.75),
-    },
-    // Midground (panels, etc)
-    300: {
-      base: sample(ramps.neutral, 1),
-      hovered: sample(ramps.neutral, 1.25),
-      active: sample(ramps.neutral, 1.5),
-    },
-    // Editor
-    500: {
-      base: sample(ramps.neutral, 0),
-      hovered: sample(ramps.neutral, 0.25),
-      active: sample(ramps.neutral, 0.5),
-    },
-    on300: {
-      base: sample(ramps.neutral, 0),
-      hovered: sample(ramps.neutral, 0.5),
-      active: sample(ramps.neutral, 1),
-    },
-    on500: {
-      base: sample(ramps.neutral, 1),
-      hovered: sample(ramps.neutral, 1.5),
-      active: sample(ramps.neutral, 2),
-    },
-    ok: {
-      base: withOpacity(sample(ramps.green, 0.5), 0.15),
-      hovered: withOpacity(sample(ramps.green, 0.5), 0.2),
-      active: withOpacity(sample(ramps.green, 0.5), 0.25),
-    },
-    error: {
-      base: withOpacity(sample(ramps.red, 0.5), 0.15),
-      hovered: withOpacity(sample(ramps.red, 0.5), 0.2),
-      active: withOpacity(sample(ramps.red, 0.5), 0.25),
-    },
-    on500Error: {
-      base: sample(ramps.red, 0.05),
-      hovered: sample(ramps.red, 0.1),
-      active: sample(ramps.red, 0.15),
-    },
-    warning: {
-      base: withOpacity(sample(ramps.yellow, 0.5), 0.15),
-      hovered: withOpacity(sample(ramps.yellow, 0.5), 0.2),
-      active: withOpacity(sample(ramps.yellow, 0.5), 0.25),
-    },
-    on500Warning: {
-      base: sample(ramps.yellow, 0.05),
-      hovered: sample(ramps.yellow, 0.1),
-      active: sample(ramps.yellow, 0.15),
-    },
-    info: {
-      base: withOpacity(sample(ramps.blue, 0.5), 0.15),
-      hovered: withOpacity(sample(ramps.blue, 0.5), 0.2),
-      active: withOpacity(sample(ramps.blue, 0.5), 0.25),
-    },
-    on500Info: {
-      base: sample(ramps.blue, 0.05),
-      hovered: sample(ramps.blue, 0.1),
-      active: sample(ramps.blue, 0.15),
-    },
-  };
-
-  const borderColor = {
-    primary: sample(ramps.neutral, isLight ? 1.5 : 0),
-    secondary: sample(ramps.neutral, isLight ? 1.25 : 1),
-    muted: sample(ramps.neutral, isLight ? 1 : 3),
-    active: sample(ramps.neutral, isLight ? 4 : 3),
-    onMedia: withOpacity(darkest, 0.1),
-    ok: sample(ramps.green, 0.3),
-    error: sample(ramps.red, 0.3),
-    warning: sample(ramps.yellow, 0.3),
-    info: sample(ramps.blue, 0.3),
-  };
-
-  const textColor = {
-    primary: sample(ramps.neutral, 6),
-    secondary: sample(ramps.neutral, 5),
-    muted: sample(ramps.neutral, 4),
-    placeholder: sample(ramps.neutral, 3),
-    active: sample(ramps.neutral, 7),
-    feature: sample(ramps.blue, 0.5),
-    ok: sample(ramps.green, 0.5),
-    error: sample(ramps.red, 0.5),
-    warning: sample(ramps.yellow, 0.5),
-    info: sample(ramps.blue, 0.5),
-    onMedia: darkest,
-  };
-
-  const player = {
-    1: buildPlayer(sample(ramps.blue, 0.5)),
-    2: buildPlayer(sample(ramps.green, 0.5)),
-    3: buildPlayer(sample(ramps.magenta, 0.5)),
-    4: buildPlayer(sample(ramps.orange, 0.5)),
-    5: buildPlayer(sample(ramps.violet, 0.5)),
-    6: buildPlayer(sample(ramps.cyan, 0.5)),
-    7: buildPlayer(sample(ramps.red, 0.5)),
-    8: buildPlayer(sample(ramps.yellow, 0.5)),
-  };
-
-  const editor = {
-    background: backgroundColor[500].base,
-    indent_guide: borderColor.muted,
-    indent_guide_active: borderColor.secondary,
-    line: {
-      active: sample(ramps.neutral, 1),
-      highlighted: sample(ramps.neutral, 1.25), // TODO: Where is this used?
-    },
-    highlight: {
-      selection: player[1].selectionColor,
-      occurrence: withOpacity(sample(ramps.neutral, 3.5), blend),
-      activeOccurrence: withOpacity(sample(ramps.neutral, 3.5), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
-      matchingBracket: backgroundColor[500].active, // TODO: Not hooked up
-      match: sample(ramps.violet, 0.15),
-      activeMatch: withOpacity(sample(ramps.violet, 0.4), blend * 2), // TODO: Not hooked up - https://github.com/zed-industries/zed/issues/751
-      related: backgroundColor[500].hovered,
-    },
-    gutter: {
-      primary: textColor.placeholder,
-      active: textColor.active,
-    },
-  };
-
-  const syntax: Syntax = {
-    primary: {
-      color: sample(ramps.neutral, 7),
-      weight: fontWeights.normal,
-    },
-    comment: {
-      color: sample(ramps.neutral, 5),
-      weight: fontWeights.normal,
-    },
-    punctuation: {
-      color: sample(ramps.neutral, 6),
-      weight: fontWeights.normal,
-    },
-    constant: {
-      color: sample(ramps.neutral, 4),
-      weight: fontWeights.normal,
-    },
-    keyword: {
-      color: sample(ramps.blue, 0.5),
-      weight: fontWeights.normal,
-    },
-    function: {
-      color: sample(ramps.yellow, 0.5),
-      weight: fontWeights.normal,
-    },
-    type: {
-      color: sample(ramps.cyan, 0.5),
-      weight: fontWeights.normal,
-    },
-    constructor: {
-      color: sample(ramps.blue, 0.5),
-      weight: fontWeights.normal,
-    },
-    variant: {
-      color: sample(ramps.blue, 0.5),
-      weight: fontWeights.normal,
-    },
-    property: {
-      color: sample(ramps.blue, 0.5),
-      weight: fontWeights.normal,
-    },
-    enum: {
-      color: sample(ramps.orange, 0.5),
-      weight: fontWeights.normal,
-    },
-    operator: {
-      color: sample(ramps.orange, 0.5),
-      weight: fontWeights.normal,
-    },
-    string: {
-      color: sample(ramps.orange, 0.5),
-      weight: fontWeights.normal,
-    },
-    number: {
-      color: sample(ramps.green, 0.5),
-      weight: fontWeights.normal,
-    },
-    boolean: {
-      color: sample(ramps.green, 0.5),
-      weight: fontWeights.normal,
-    },
-    predictive: {
-      color: textColor.muted,
-      weight: fontWeights.normal,
-    },
-    title: {
-      color: sample(ramps.yellow, 0.5),
-      weight: fontWeights.bold,
-    },
-    emphasis: {
-      color: textColor.feature,
-      weight: fontWeights.normal,
-    },
-    "emphasis.strong": {
-      color: textColor.feature,
-      weight: fontWeights.bold,
-    },
-    linkUri: {
-      color: sample(ramps.green, 0.5),
-      weight: fontWeights.normal,
-      underline: true,
-    },
-    linkText: {
-      color: sample(ramps.orange, 0.5),
-      weight: fontWeights.normal,
-      italic: true,
-    },
-  };
-
-  const shadow = withOpacity(
-    ramps
-      .neutral(isLight ? 7 : 0)
-      .darken()
-      .hex(),
-    blend
-  );
-
-  return {
-    name,
-    isLight,
-    backgroundColor,
-    borderColor,
-    textColor,
-    iconColor: textColor,
-    editor,
-    syntax,
-    player,
-    shadow,
-    ramps,
-  };
-}

styles/src/themes/common/colorScheme.ts πŸ”—

@@ -0,0 +1,83 @@
+import { Scale } from "chroma-js";
+
+export interface ColorScheme {
+  name: string,
+  isLight: boolean,
+
+  lowest: Elevation,
+  middle: Elevation,
+  highest: Elevation,
+
+  players: Players,
+}
+
+export interface Player {
+  cursor: string,
+  selection: string,
+}
+
+export interface Players {
+  "0": Player,
+  "1": Player,
+  "2": Player,
+  "3": Player,
+  "4": Player,
+  "5": Player,
+  "6": Player,
+  "7": Player,
+}
+
+export interface Elevation {
+  ramps: RampSet,
+
+  bottom: Layer,
+  middle: Layer,
+  top: Layer,
+
+  above?: Elevation,
+  shadow?: Shadow
+}
+
+export interface Shadow {
+  blur: number,
+  color: string,
+  offset: number[],
+}
+
+export type StyleSets = keyof Layer;
+export interface Layer {
+  base: StyleSet,
+  on: StyleSet,
+  info: StyleSet,
+  positive: StyleSet,
+  warning: StyleSet,
+  negative: StyleSet,
+}
+
+export interface RampSet {
+  neutral: Scale,
+  red: Scale,
+  orange: Scale,
+  yellow: Scale,
+  green: Scale,
+  cyan: Scale,
+  blue: Scale,
+  violet: Scale,
+  magenta: Scale,
+}
+
+export type Styles = keyof StyleSet;
+export interface StyleSet {
+  default: Style,
+  variant: Style,
+  active: Style,
+  disabled: Style,
+  hovered: Style,
+  pressed: Style,
+}
+
+export interface Style {
+  background: string,
+  border: string,
+  foreground: string,
+}

styles/src/themes/common/ramps.ts πŸ”—

@@ -0,0 +1,176 @@
+import chroma, { Color, Scale } from "chroma-js";
+import { ColorScheme, Elevation, Layer, Player, RampSet, Shadow, Style, StyleSet } from "./colorScheme";
+
+export function colorRamp(color: Color): Scale {
+  let hue = color.hsl()[0];
+  let endColor = chroma.hsl(hue, 0.88, 0.96);
+  let startColor = chroma.hsl(hue, 0.68, 0.12);
+  return chroma.scale([startColor, color, endColor]).mode("hsl");
+}
+
+export function createColorScheme(name: string, isLight: boolean, colorRamps: { [rampName: string]: Scale }): ColorScheme {
+  // Chromajs scales from 0 to 1 flipped if isLight is true
+  let baseRamps: typeof colorRamps = {};
+
+  // Chromajs mutates the underlying ramp when you call domain. This causes problems because
+  // we now store the ramps object in the theme so that we can pull colors out of them.
+  // So instead of calling domain and storing the result, we have to construct new ramps for each
+  // theme so that we don't modify the passed in ramps.
+  // This combined with an error in the type definitions for chroma js means we have to cast the colors
+  // function to any in order to get the colors back out from the original ramps.
+  if (isLight) {
+    for (var rampName in colorRamps) {
+      baseRamps[rampName] = chroma
+        .scale((colorRamps[rampName].colors as any)())
+        .domain([1, 0]);
+    }
+    baseRamps.neutral = chroma
+      .scale((colorRamps.neutral.colors as any)())
+      .domain([1, 0]);
+  } else {
+    for (var rampName in colorRamps) {
+      baseRamps[rampName] = chroma
+        .scale((colorRamps[rampName].colors as any)())
+        .domain([0, 1]);
+    }
+    baseRamps.neutral = chroma
+      .scale((colorRamps.neutral.colors as any)())
+      .domain([0, 1]);
+  }
+
+  let baseSet = {
+    neutral: baseRamps.neutral,
+    red: baseRamps.red,
+    orange: baseRamps.orange,
+    yellow: baseRamps.yellow,
+    green: baseRamps.green,
+    cyan: baseRamps.cyan,
+    blue: baseRamps.blue,
+    violet: baseRamps.violet,
+    magenta: baseRamps.magenta,
+  };
+
+  let lowest = elevation(
+    resampleSet(
+      baseSet,
+      evenSamples(0, 1)
+    ),
+    isLight,
+  );
+
+  let middle = elevation(
+    resampleSet(
+      baseSet,
+      evenSamples(0.125, 1)
+    ),
+    isLight,
+    {
+      blur: 4,
+      color: baseSet.neutral(isLight ? 7 : 0).darken().alpha(0.2).hex(), // TODO used blend previously. Replace with something else
+      offset: [1, 2],
+    }
+  );
+  lowest.above = middle;
+
+  let highest = elevation(
+    resampleSet(
+      baseSet,
+      evenSamples(0.25, 1)
+    ),
+    isLight,
+    {
+      blur: 16,
+      color: baseSet.neutral(isLight ? 7 : 0).darken().alpha(0.2).hex(), // TODO used blend previously. Replace with something else
+      offset: [0, 2],
+    }
+  );
+  middle.above = highest;
+
+  let players = {
+    "0": player(baseSet.blue),
+    "1": player(baseSet.green),
+    "2": player(baseSet.magenta),
+    "3": player(baseSet.orange),
+    "4": player(baseSet.violet),
+    "5": player(baseSet.cyan),
+    "6": player(baseSet.red),
+    "7": player(baseSet.yellow),
+  }
+
+  return {
+    name,
+    isLight,
+
+    lowest,
+    middle,
+    highest,
+
+    players,
+  };
+}
+
+function player(ramp: Scale): Player {
+  return {
+    selection: ramp(0.5).alpha(0.24).hex(),
+    cursor: ramp(0.5).hex(),
+  }
+}
+
+function evenSamples(min: number, max: number): number[] {
+  return Array.from(Array(101).keys()).map((i) => (i / 100) * (max - min) + min);
+}
+
+function resampleSet(ramps: RampSet, samples: number[]): RampSet {
+  return {
+    neutral: resample(ramps.neutral, samples),
+    red: resample(ramps.neutral, samples),
+    orange: resample(ramps.neutral, samples),
+    yellow: resample(ramps.neutral, samples),
+    green: resample(ramps.neutral, samples),
+    cyan: resample(ramps.neutral, samples),
+    blue: resample(ramps.neutral, samples),
+    violet: resample(ramps.neutral, samples),
+    magenta: resample(ramps.neutral, samples),
+  }
+}
+
+function resample(scale: Scale, samples: number[]): Scale {
+  let newColors = samples.map((sample) => scale(sample));
+  return chroma.scale(newColors);
+}
+
+function elevation(ramps: RampSet, isLight: boolean, shadow?: Shadow): Elevation {
+  let style: Style = {
+    background: ramps.neutral(0.25).hex(),
+    border: ramps.neutral(0.9).hex(),
+    foreground: ramps.neutral(1).hex(),
+  };
+
+  let styleSet: StyleSet = {
+    default: style,
+    variant: style,
+    active: style,
+    disabled: style,
+    hovered: style,
+    pressed: style,
+  };
+
+  let layer: Layer = {
+    base: styleSet,
+    on: styleSet,
+    info: styleSet,
+    positive: styleSet,
+    warning: styleSet,
+    negative: styleSet
+  };
+
+  return {
+    ramps,
+
+    bottom: layer,
+    middle: layer,
+    top: layer,
+
+    shadow,
+  };
+}

styles/src/themes/common/theme.ts πŸ”—

@@ -1,164 +0,0 @@
-import { Scale } from "chroma-js";
-import { FontWeight } from "../../common";
-import { withOpacity } from "../../utils/color";
-
-export interface SyntaxHighlightStyle {
-  color: string;
-  weight?: FontWeight;
-  underline?: boolean;
-  italic?: boolean;
-}
-
-export interface Player {
-  baseColor: string;
-  cursorColor: string;
-  selectionColor: string;
-  borderColor: string;
-}
-export function buildPlayer(
-  color: string,
-  cursorOpacity?: number,
-  selectionOpacity?: number,
-  borderOpacity?: number
-) {
-  return {
-    baseColor: color,
-    cursorColor: withOpacity(color, cursorOpacity || 1.0),
-    selectionColor: withOpacity(color, selectionOpacity || 0.24),
-    borderColor: withOpacity(color, borderOpacity || 0.8),
-  };
-}
-
-export interface BackgroundColorSet {
-  base: string;
-  hovered: string;
-  active: string;
-}
-
-export interface Syntax {
-  primary: SyntaxHighlightStyle;
-  comment: SyntaxHighlightStyle;
-  punctuation: SyntaxHighlightStyle;
-  constant: SyntaxHighlightStyle;
-  keyword: SyntaxHighlightStyle;
-  function: SyntaxHighlightStyle;
-  type: SyntaxHighlightStyle;
-  variant: SyntaxHighlightStyle;
-  property: SyntaxHighlightStyle;
-  enum: SyntaxHighlightStyle;
-  operator: SyntaxHighlightStyle;
-  string: SyntaxHighlightStyle;
-  number: SyntaxHighlightStyle;
-  boolean: SyntaxHighlightStyle;
-  predictive: SyntaxHighlightStyle;
-  title: SyntaxHighlightStyle;
-  emphasis: SyntaxHighlightStyle;
-  linkUri: SyntaxHighlightStyle;
-  linkText: SyntaxHighlightStyle;
-
-  [key: string]: SyntaxHighlightStyle;
-}
-
-export default interface Theme {
-  name: string;
-  isLight: boolean;
-  backgroundColor: {
-    // Basically just Title Bar
-    // Lowest background level
-    100: BackgroundColorSet;
-    // Tab bars, panels, popovers
-    // Mid-ground
-    300: BackgroundColorSet;
-    // The editor
-    // Foreground
-    500: BackgroundColorSet;
-    // Hacks for elements on top of the midground
-    // Buttons in a panel, tab bar, or panel
-    on300: BackgroundColorSet;
-    // Hacks for elements on top of the editor
-    on500: BackgroundColorSet;
-    ok: BackgroundColorSet;
-    error: BackgroundColorSet;
-    on500Error: BackgroundColorSet;
-    warning: BackgroundColorSet;
-    on500Warning: BackgroundColorSet;
-    info: BackgroundColorSet;
-    on500Info: BackgroundColorSet;
-  };
-  borderColor: {
-    primary: string;
-    secondary: string;
-    muted: string;
-    active: string;
-    /**
-     * Used for rendering borders on top of media like avatars, images, video, etc.
-     */
-    onMedia: string;
-    ok: string;
-    error: string;
-    warning: string;
-    info: string;
-  };
-  textColor: {
-    primary: string;
-    secondary: string;
-    muted: string;
-    placeholder: string;
-    active: string;
-    feature: string;
-    ok: string;
-    error: string;
-    warning: string;
-    info: string;
-    onMedia: string;
-  };
-  iconColor: {
-    primary: string;
-    secondary: string;
-    muted: string;
-    placeholder: string;
-    active: string;
-    feature: string;
-    ok: string;
-    error: string;
-    warning: string;
-    info: string;
-  };
-  editor: {
-    background: string;
-    indent_guide: string;
-    indent_guide_active: string;
-    line: {
-      active: string;
-      highlighted: string;
-    };
-    highlight: {
-      selection: string;
-      occurrence: string;
-      activeOccurrence: string;
-      matchingBracket: string;
-      match: string;
-      activeMatch: string;
-      related: string;
-    };
-    gutter: {
-      primary: string;
-      active: string;
-    };
-  };
-
-  syntax: Syntax;
-
-  player: {
-    1: Player;
-    2: Player;
-    3: Player;
-    4: Player;
-    5: Player;
-    6: Player;
-    7: Player;
-    8: Player;
-  };
-  shadow: string;
-  ramps: { [rampName: string]: Scale };
-}

styles/src/themes/one-dark.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "one";
 const author = "Chris Kempson (http://chriskempson.com)";
@@ -44,4 +44,4 @@ const ramps = {
   magenta: colorRamp(chroma(base0F)),
 };
 
-export const dark = createTheme(`${name}-dark`, false, ramps);
+export const dark = createColorScheme(`${name}-dark`, false, ramps);

styles/src/themes/one-light.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "one";
 const author = "Daniel Pfeifer (http://github.com/purpleKarrot)";
@@ -44,4 +44,4 @@ const ramps = {
   magenta: colorRamp(chroma(base0F)),
 };
 
-export const light = createTheme(`${name}-light`, true, ramps);
+export const light = createColorScheme(`${name}-light`, true, ramps);

styles/src/themes/rose-pine-dawn.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "rosΓ©-pine-dawn";
 
@@ -24,4 +24,4 @@ const ramps = {
   magenta: colorRamp(chroma("#79549F")),
 };
 
-export const light = createTheme(`${name}`, true, ramps);
+export const light = createColorScheme(`${name}`, true, ramps);

styles/src/themes/rose-pine-moon.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "rosΓ©-pine-moon";
 
@@ -24,4 +24,4 @@ const ramps = {
   magenta: colorRamp(chroma("#AB6FE9")),
 };
 
-export const dark = createTheme(`${name}`, false, ramps);
+export const dark = createColorScheme(`${name}`, false, ramps);

styles/src/themes/rose-pine.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "rosΓ©-pine";
 
@@ -24,4 +24,4 @@ const ramps = {
   magenta: colorRamp(chroma("#AB6FE9")),
 };
 
-export const dark = createTheme(`${name}`, false, ramps);
+export const dark = createColorScheme(`${name}`, false, ramps);

styles/src/themes/sandcastle.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "sandcastle";
 
@@ -24,4 +24,4 @@ const ramps = {
   magenta: colorRamp(chroma("#a87322")),
 };
 
-export const dark = createTheme(`${name}`, false, ramps);
+export const dark = createColorScheme(`${name}`, false, ramps);

styles/src/themes/solarized.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "solarized";
 
@@ -24,5 +24,5 @@ const ramps = {
   magenta: colorRamp(chroma("#d33682")),
 };
 
-export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const dark = createColorScheme(`${name}-dark`, false, ramps);
+export const light = createColorScheme(`${name}-light`, true, ramps);

styles/src/themes/sulphurpool.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "sulphurpool";
 
@@ -24,5 +24,5 @@ const ramps = {
   magenta: colorRamp(chroma("#9c637a")),
 };
 
-export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const dark = createColorScheme(`${name}-dark`, false, ramps);
+export const light = createColorScheme(`${name}-light`, true, ramps);

styles/src/themes/summercamp.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "summercamp";
 
@@ -24,4 +24,4 @@ const ramps = {
   magenta: colorRamp(chroma("#F69BE7")),
 };
 
-export const dark = createTheme(`${name}`, false, ramps);
+export const dark = createColorScheme(`${name}`, false, ramps);

styles/src/themes/summerfruit.ts πŸ”—

@@ -1,5 +1,5 @@
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 const name = "summerfruit";
 
@@ -24,5 +24,5 @@ const ramps = {
   magenta: colorRamp(chroma("#CC6633")),
 };
 
-export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const dark = createColorScheme(`${name}-dark`, false, ramps);
+export const light = createColorScheme(`${name}-light`, true, ramps);

styles/src/themes/template.ts πŸ”—

@@ -3,7 +3,7 @@
  **/
 
 import chroma from "chroma-js";
-import { colorRamp, createTheme } from "./common/base16";
+import { colorRamp, createColorScheme } from "./common/ramps";
 
 /**
  * Theme Name
@@ -56,14 +56,14 @@ const ramps = {
 };
 
 /**
- * Theme Variants
+ * Color Scheme Variants
  *
  * Currently we only support (and require) dark and light themes
  * Eventually you will be able to have only a light or dark theme,
  * and define other variants here.
  *
- * createTheme([name], [isLight], [arrayOfRamps])
+ * createColorScheme([name], [isLight], [arrayOfRamps])
  **/
 
-export const dark = createTheme(`${name}-dark`, false, ramps);
-export const light = createTheme(`${name}-light`, true, ramps);
+export const dark = createColorScheme(`${name}-dark`, false, ramps);
+export const light = createColorScheme(`${name}-light`, true, ramps);