add theme testbench command

K Simmons created

Change summary

Cargo.lock                                     |  13 
crates/terminal/Cargo.toml                     |   1 
crates/terminal/src/terminal_container_view.rs |  11 
crates/theme/src/theme.rs                      |  72 ++
crates/theme_testbench/Cargo.toml              |  18 
crates/theme_testbench/src/theme_testbench.rs  | 506 ++++++++++++++++++++
crates/zed/Cargo.toml                          |   1 
crates/zed/src/main.rs                         |   1 
styles/src/styleTree/app.ts                    |  55 ++
styles/src/styleTree/editor.ts                 |   4 
styles/src/themes/common/ramps.ts              |  89 ++-
11 files changed, 728 insertions(+), 43 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -5551,6 +5551,18 @@ dependencies = [
  "workspace",
 ]
 
+[[package]]
+name = "theme_testbench"
+version = "0.1.0"
+dependencies = [
+ "gpui",
+ "project",
+ "settings",
+ "smallvec",
+ "theme",
+ "workspace",
+]
+
 [[package]]
 name = "thiserror"
 version = "1.0.31"
@@ -7180,6 +7192,7 @@ dependencies = [
  "text",
  "theme",
  "theme_selector",
+ "theme_testbench",
  "thiserror",
  "tiny_http",
  "toml",

crates/terminal/Cargo.toml 🔗

@@ -30,7 +30,6 @@ libc = "0.2"
 anyhow = "1"
 thiserror = "1.0"
 
-
 [dev-dependencies]
 gpui = { path = "../gpui", features = ["test-support"] }
 client = { path = "../client", features = ["test-support"]}

crates/terminal/src/terminal_container_view.rs 🔗

@@ -45,7 +45,6 @@ impl TerminalContainerContent {
 }
 
 pub struct TerminalContainer {
-    modal: bool,
     pub content: TerminalContainerContent,
     associated_directory: Option<PathBuf>,
 }
@@ -133,7 +132,6 @@ impl TerminalContainer {
         cx.focus(content.handle());
 
         TerminalContainer {
-            modal,
             content,
             associated_directory: working_directory,
         }
@@ -146,7 +144,6 @@ impl TerminalContainer {
     ) -> Self {
         let connected_view = cx.add_view(|cx| TerminalView::from_terminal(terminal, modal, cx));
         TerminalContainer {
-            modal,
             content: TerminalContainerContent::Connected(connected_view),
             associated_directory: None,
         }
@@ -178,14 +175,6 @@ impl View for TerminalContainer {
             cx.focus(self.content.handle());
         }
     }
-
-    fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context {
-        let mut context = Self::default_keymap_context();
-        if self.modal {
-            context.set.insert("ModalTerminal".into());
-        }
-        context
-    }
 }
 
 impl View for ErrorView {

crates/theme/src/theme.rs 🔗

@@ -2,7 +2,7 @@ mod theme_registry;
 
 use gpui::{
     color::Color,
-    elements::{ContainerStyle, ImageStyle, LabelStyle, TooltipStyle},
+    elements::{ContainerStyle, ImageStyle, LabelStyle, Shadow, TooltipStyle},
     fonts::{HighlightStyle, TextStyle},
     Border, MouseState,
 };
@@ -32,6 +32,7 @@ pub struct Theme {
     pub update_notification: UpdateNotification,
     pub tooltip: TooltipStyle,
     pub terminal: TerminalStyle,
+    pub color_scheme: ColorScheme,
 }
 
 #[derive(Deserialize, Default, Clone)]
@@ -728,3 +729,72 @@ pub struct TerminalStyle {
     pub bright_foreground: Color,
     pub dim_foreground: Color,
 }
+
+#[derive(Clone, Deserialize, Default)]
+pub struct ColorScheme {
+    pub name: String,
+    pub is_light: bool,
+
+    pub lowest: Elevation,
+    pub middle: Elevation,
+    pub highest: Elevation,
+
+    pub players: Vec<Player>,
+}
+
+#[derive(Clone, Deserialize, Default)]
+pub struct Elevation {
+    pub ramps: RampSet,
+
+    pub bottom: Layer,
+    pub middle: Layer,
+    pub top: Layer,
+
+    pub shadow: Option<Shadow>,
+}
+
+#[derive(Clone, Deserialize, Default)]
+pub struct Player {
+    pub cursor: Color,
+    pub selection: Color,
+}
+
+#[derive(Clone, Deserialize, Default)]
+pub struct RampSet {
+    pub neutral: Vec<Color>,
+    pub red: Vec<Color>,
+    pub orange: Vec<Color>,
+    pub yellow: Vec<Color>,
+    pub green: Vec<Color>,
+    pub cyan: Vec<Color>,
+    pub blue: Vec<Color>,
+    pub violet: Vec<Color>,
+    pub magenta: Vec<Color>,
+}
+
+#[derive(Clone, Deserialize, Default)]
+pub struct Layer {
+    pub base: StyleSet,
+    pub on: StyleSet,
+    pub info: StyleSet,
+    pub positive: StyleSet,
+    pub warning: StyleSet,
+    pub negative: StyleSet,
+}
+
+#[derive(Clone, Deserialize, Default)]
+pub struct StyleSet {
+    pub default: Style,
+    pub variant: Style,
+    pub active: Style,
+    pub disabled: Style,
+    pub hovered: Style,
+    pub pressed: Style,
+}
+
+#[derive(Clone, Deserialize, Default)]
+pub struct Style {
+    pub background: Color,
+    pub border: Color,
+    pub foreground: Color,
+}

crates/theme_testbench/Cargo.toml 🔗

@@ -0,0 +1,18 @@
+[package]
+name = "theme_testbench"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+path = "src/theme_testbench.rs"
+doctest = false
+
+
+[dependencies]
+gpui = { path = "../gpui" }
+theme = { path = "../theme" }
+settings = { path = "../settings" }
+workspace = { path = "../workspace" }
+project = { path = "../project" }
+
+smallvec = { version = "1.6", features = ["union"] }

crates/theme_testbench/src/theme_testbench.rs 🔗

@@ -0,0 +1,506 @@
+use gpui::{
+    actions,
+    color::Color,
+    elements::{
+        Canvas, ConstrainedBox, Container, ContainerStyle, ElementBox, Flex, Label, Margin,
+        MouseEventHandler, Padding, ParentElement,
+    },
+    fonts::TextStyle,
+    Border, Element, Entity, MutableAppContext, Quad, RenderContext, View, ViewContext,
+};
+use project::{Project, ProjectEntryId, ProjectPath};
+use settings::Settings;
+use smallvec::SmallVec;
+use theme::{ColorScheme, Elevation, Layer, Style, StyleSet};
+use workspace::{Item, Workspace};
+
+actions!(theme, [DeployThemeTestbench]);
+
+pub fn init(cx: &mut MutableAppContext) {
+    cx.add_action(ThemeTestbench::deploy);
+}
+
+pub struct ThemeTestbench {}
+
+impl ThemeTestbench {
+    pub fn deploy(
+        workspace: &mut Workspace,
+        _: &DeployThemeTestbench,
+        cx: &mut ViewContext<Workspace>,
+    ) {
+        let view = cx.add_view(|_| ThemeTestbench {});
+        workspace.add_item(Box::new(view), cx);
+    }
+
+    fn render_ramps(color_scheme: &ColorScheme) -> Flex {
+        fn display_ramp(ramp: &Vec<Color>) -> ElementBox {
+            Flex::row()
+                .with_children(ramp.iter().cloned().map(|color| {
+                    Canvas::new(move |bounds, _, cx| {
+                        cx.scene.push_quad(Quad {
+                            bounds,
+                            background: Some(color),
+                            ..Default::default()
+                        });
+                    })
+                    .flex(1.0, false)
+                    .boxed()
+                }))
+                .flex(1.0, false)
+                .boxed()
+        }
+
+        Flex::column()
+            .with_child(display_ramp(&color_scheme.lowest.ramps.neutral))
+            .with_child(display_ramp(&color_scheme.lowest.ramps.red))
+            .with_child(display_ramp(&color_scheme.lowest.ramps.orange))
+            .with_child(display_ramp(&color_scheme.lowest.ramps.yellow))
+            .with_child(display_ramp(&color_scheme.lowest.ramps.green))
+            .with_child(display_ramp(&color_scheme.lowest.ramps.cyan))
+            .with_child(display_ramp(&color_scheme.lowest.ramps.blue))
+            .with_child(display_ramp(&color_scheme.lowest.ramps.violet))
+            .with_child(display_ramp(&color_scheme.lowest.ramps.magenta))
+    }
+
+    fn render_elevation(
+        elevation_index: usize,
+        elevation: &Elevation,
+        cx: &mut RenderContext<'_, Self>,
+    ) -> Flex {
+        Flex::column()
+            .with_child(
+                Self::render_layer(elevation_index * 1000 + 100, &elevation.bottom, cx)
+                    .flex(1., true)
+                    .boxed(),
+            )
+            .with_child(
+                Self::render_layer(elevation_index * 1000 + 200, &elevation.middle, cx)
+                    .flex(1., true)
+                    .boxed(),
+            )
+            .with_child(
+                Self::render_layer(elevation_index * 1000 + 300, &elevation.top, cx)
+                    .flex(1., true)
+                    .boxed(),
+            )
+    }
+
+    fn render_layer(
+        layer_index: usize,
+        layer: &Layer,
+        cx: &mut RenderContext<'_, Self>,
+    ) -> Container {
+        Flex::column()
+            .with_child(
+                Flex::row()
+                    .with_child(Self::render_button(
+                        0,
+                        layer_index,
+                        "base",
+                        &layer.base,
+                        None,
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        1,
+                        layer_index,
+                        "active",
+                        &layer.base,
+                        Some(|style_set| &style_set.active),
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        2,
+                        layer_index,
+                        "disabled",
+                        &layer.base,
+                        Some(|style_set| &style_set.disabled),
+                        cx,
+                    ))
+                    .flex(1., false)
+                    .boxed(),
+            )
+            .with_child(
+                Flex::row()
+                    .with_child(Self::render_button(
+                        3,
+                        layer_index,
+                        "on",
+                        &layer.on,
+                        None,
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        4,
+                        layer_index,
+                        "active",
+                        &layer.on,
+                        Some(|style_set| &style_set.active),
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        5,
+                        layer_index,
+                        "disabled",
+                        &layer.on,
+                        Some(|style_set| &style_set.disabled),
+                        cx,
+                    ))
+                    .flex(1., false)
+                    .boxed(),
+            )
+            .with_child(
+                Flex::row()
+                    .with_child(Self::render_button(
+                        6,
+                        layer_index,
+                        "info",
+                        &layer.info,
+                        None,
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        7,
+                        layer_index,
+                        "active",
+                        &layer.info,
+                        Some(|style_set| &style_set.active),
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        8,
+                        layer_index,
+                        "disabled",
+                        &layer.info,
+                        Some(|style_set| &style_set.disabled),
+                        cx,
+                    ))
+                    .flex(1., false)
+                    .boxed(),
+            )
+            .with_child(
+                Flex::row()
+                    .with_child(Self::render_button(
+                        9,
+                        layer_index,
+                        "positive",
+                        &layer.positive,
+                        None,
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        10,
+                        layer_index,
+                        "active",
+                        &layer.positive,
+                        Some(|style_set| &style_set.active),
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        11,
+                        layer_index,
+                        "disabled",
+                        &layer.positive,
+                        Some(|style_set| &style_set.disabled),
+                        cx,
+                    ))
+                    .flex(1., false)
+                    .boxed(),
+            )
+            .with_child(
+                Flex::row()
+                    .with_child(Self::render_button(
+                        12,
+                        layer_index,
+                        "warning",
+                        &layer.warning,
+                        None,
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        13,
+                        layer_index,
+                        "active",
+                        &layer.warning,
+                        Some(|style_set| &style_set.active),
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        14,
+                        layer_index,
+                        "disabled",
+                        &layer.warning,
+                        Some(|style_set| &style_set.disabled),
+                        cx,
+                    ))
+                    .flex(1., false)
+                    .boxed(),
+            )
+            .with_child(
+                Flex::row()
+                    .with_child(Self::render_button(
+                        15,
+                        layer_index,
+                        "negative",
+                        &layer.negative,
+                        None,
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        16,
+                        layer_index,
+                        "active",
+                        &layer.negative,
+                        Some(|style_set| &style_set.active),
+                        cx,
+                    ))
+                    .with_child(Self::render_button(
+                        17,
+                        layer_index,
+                        "disabled",
+                        &layer.negative,
+                        Some(|style_set| &style_set.disabled),
+                        cx,
+                    ))
+                    .flex(1., false)
+                    .boxed(),
+            )
+            .contained()
+            .with_style(ContainerStyle {
+                margin: Margin {
+                    top: 10.,
+                    bottom: 10.,
+                    left: 10.,
+                    right: 10.,
+                },
+                background_color: Some(layer.base.default.background),
+                ..Default::default()
+            })
+    }
+
+    fn render_button(
+        button_index: usize,
+        layer_index: usize,
+        text: &'static str,
+        style_set: &StyleSet,
+        style_override: Option<fn(&StyleSet) -> &Style>,
+        cx: &mut RenderContext<'_, Self>,
+    ) -> ElementBox {
+        enum TestBenchButton {}
+        MouseEventHandler::<TestBenchButton>::new(layer_index + button_index, cx, |state, cx| {
+            let style = if let Some(style_override) = style_override {
+                style_override(&style_set)
+            } else if state.clicked.is_some() {
+                &style_set.pressed
+            } else if state.hovered {
+                &style_set.hovered
+            } else {
+                &style_set.default
+            };
+
+            Self::render_label(text.to_string(), style, cx)
+                .contained()
+                .with_style(ContainerStyle {
+                    margin: Margin {
+                        top: 4.,
+                        bottom: 4.,
+                        left: 4.,
+                        right: 4.,
+                    },
+                    padding: Padding {
+                        top: 4.,
+                        bottom: 4.,
+                        left: 4.,
+                        right: 4.,
+                    },
+                    background_color: Some(style.background),
+                    border: Border {
+                        width: 1.,
+                        color: style.border,
+                        overlay: false,
+                        top: true,
+                        bottom: true,
+                        left: true,
+                        right: true,
+                    },
+                    corner_radius: 2.,
+                    ..Default::default()
+                })
+                .boxed()
+        })
+        .flex(1., true)
+        .boxed()
+    }
+
+    fn render_label(text: String, style: &Style, cx: &mut RenderContext<'_, Self>) -> Label {
+        let settings = cx.global::<Settings>();
+        let font_cache = cx.font_cache();
+        let family_id = settings.buffer_font_family;
+        let font_size = settings.buffer_font_size;
+        let font_id = font_cache
+            .select_font(family_id, &Default::default())
+            .unwrap();
+
+        let text_style = TextStyle {
+            color: style.foreground,
+            font_family_id: family_id,
+            font_family_name: font_cache.family_name(family_id).unwrap(),
+            font_id,
+            font_size,
+            font_properties: Default::default(),
+            underline: Default::default(),
+        };
+
+        Label::new(text, text_style)
+    }
+
+    fn elevation_style(elevation: &Elevation) -> ContainerStyle {
+        let style = ContainerStyle {
+            margin: Margin {
+                top: 10.,
+                bottom: 10.,
+                left: 10.,
+                right: 10.,
+            },
+            background_color: Some(elevation.bottom.base.default.background),
+            ..Default::default()
+        };
+
+        if elevation.shadow.is_some() {
+            ContainerStyle {
+                padding: Padding {
+                    top: 10.,
+                    bottom: 10.,
+                    left: 10.,
+                    right: 10.,
+                },
+                border: Border {
+                    width: 1.,
+                    color: elevation.bottom.base.default.border,
+                    overlay: false,
+                    top: true,
+                    bottom: true,
+                    left: true,
+                    right: true,
+                },
+                corner_radius: 32.,
+                shadow: elevation.shadow,
+                ..style
+            }
+        } else {
+            style
+        }
+    }
+}
+
+impl Entity for ThemeTestbench {
+    type Event = ();
+}
+
+impl View for ThemeTestbench {
+    fn ui_name() -> &'static str {
+        "ThemeTestbench"
+    }
+
+    fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox {
+        let color_scheme = &cx.global::<Settings>().theme.clone().color_scheme;
+
+        Flex::row()
+            .with_child(
+                Self::render_ramps(color_scheme)
+                    .contained()
+                    .with_margin_right(10.)
+                    .flex(0.2, false)
+                    .boxed(),
+            )
+            .with_child(
+                Self::render_elevation(0, &color_scheme.lowest, cx)
+                    .flex(1., true)
+                    .boxed(),
+            )
+            .with_child(
+                Flex::row()
+                    .with_child(
+                        Self::render_elevation(1, &color_scheme.middle, cx)
+                            .flex(1., true)
+                            .boxed(),
+                    )
+                    .with_child(
+                        Container::new(
+                            Self::render_elevation(2, &color_scheme.highest, cx).boxed(),
+                        )
+                        .with_style(Self::elevation_style(&color_scheme.highest))
+                        .flex(1., true)
+                        .boxed(),
+                    )
+                    .contained()
+                    .with_style(Self::elevation_style(&color_scheme.middle))
+                    .flex(2., true)
+                    .boxed(),
+            )
+            .contained()
+            .with_style(Self::elevation_style(&color_scheme.lowest))
+            .boxed()
+    }
+}
+
+impl Item for ThemeTestbench {
+    fn tab_content(
+        &self,
+        _: Option<usize>,
+        style: &theme::Tab,
+        _: &gpui::AppContext,
+    ) -> gpui::ElementBox {
+        Label::new("Theme Testbench".into(), style.label.clone())
+            .aligned()
+            .contained()
+            .boxed()
+    }
+
+    fn project_path(&self, _: &gpui::AppContext) -> Option<ProjectPath> {
+        None
+    }
+
+    fn project_entry_ids(&self, _: &gpui::AppContext) -> SmallVec<[ProjectEntryId; 3]> {
+        SmallVec::new()
+    }
+
+    fn is_singleton(&self, _: &gpui::AppContext) -> bool {
+        false
+    }
+
+    fn set_nav_history(&mut self, _: workspace::ItemNavHistory, _: &mut ViewContext<Self>) {}
+
+    fn can_save(&self, _: &gpui::AppContext) -> bool {
+        false
+    }
+
+    fn save(
+        &mut self,
+        _: gpui::ModelHandle<Project>,
+        _: &mut ViewContext<Self>,
+    ) -> gpui::Task<gpui::anyhow::Result<()>> {
+        unreachable!("save should not have been called");
+    }
+
+    fn save_as(
+        &mut self,
+        _: gpui::ModelHandle<Project>,
+        _: std::path::PathBuf,
+        _: &mut ViewContext<Self>,
+    ) -> gpui::Task<gpui::anyhow::Result<()>> {
+        unreachable!("save_as should not have been called");
+    }
+
+    fn reload(
+        &mut self,
+        _: gpui::ModelHandle<Project>,
+        _: &mut ViewContext<Self>,
+    ) -> gpui::Task<gpui::anyhow::Result<()>> {
+        gpui::Task::ready(Ok(()))
+    }
+
+    fn to_item_events(_: &Self::Event) -> Vec<workspace::ItemEvent> {
+        Vec::new()
+    }
+}

crates/zed/Cargo.toml 🔗

@@ -50,6 +50,7 @@ text = { path = "../text" }
 terminal = { path = "../terminal" }
 theme = { path = "../theme" }
 theme_selector = { path = "../theme_selector" }
+theme_testbench = { path = "../theme_testbench" }
 util = { path = "../util" }
 vim = { path = "../vim" }
 workspace = { path = "../workspace" }

crates/zed/src/main.rs 🔗

@@ -119,6 +119,7 @@ fn main() {
         search::init(cx);
         vim::init(cx);
         terminal::init(cx);
+        theme_testbench::init(cx);
 
         let db = cx.background().block(db);
         cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx))

styles/src/styleTree/app.ts 🔗

@@ -47,5 +47,60 @@ export default function app(colorScheme: ColorScheme): Object {
     updateNotification: updateNotification(colorScheme),
     tooltip: tooltip(colorScheme),
     terminal: terminal(colorScheme.lowest),
+    colorScheme: {
+      ...colorScheme,
+      lowest: {
+        ...colorScheme.lowest,
+        ramps: {
+          neutral: colorScheme.lowest.ramps.neutral.colors(100, "hex"),
+          red: colorScheme.lowest.ramps.red.colors(100, "hex"),
+          orange: colorScheme.lowest.ramps.orange.colors(100, "hex"),
+          yellow: colorScheme.lowest.ramps.yellow.colors(100, "hex"),
+          green: colorScheme.lowest.ramps.green.colors(100, "hex"),
+          cyan: colorScheme.lowest.ramps.cyan.colors(100, "hex"),
+          blue: colorScheme.lowest.ramps.blue.colors(100, "hex"),
+          violet: colorScheme.lowest.ramps.violet.colors(100, "hex"),
+          magenta: colorScheme.lowest.ramps.magenta.colors(100, "hex"),
+        }
+      },
+      middle: {
+        ...colorScheme.middle,
+        ramps: {
+          neutral: colorScheme.middle.ramps.neutral.colors(100, "hex"),
+          red: colorScheme.middle.ramps.red.colors(100, "hex"),
+          orange: colorScheme.middle.ramps.orange.colors(100, "hex"),
+          yellow: colorScheme.middle.ramps.yellow.colors(100, "hex"),
+          green: colorScheme.middle.ramps.green.colors(100, "hex"),
+          cyan: colorScheme.middle.ramps.cyan.colors(100, "hex"),
+          blue: colorScheme.middle.ramps.blue.colors(100, "hex"),
+          violet: colorScheme.middle.ramps.violet.colors(100, "hex"),
+          magenta: colorScheme.middle.ramps.magenta.colors(100, "hex"),
+        }
+      },
+      highest: {
+        ...colorScheme.highest,
+        ramps: {
+          neutral: colorScheme.highest.ramps.neutral.colors(100, "hex"),
+          red: colorScheme.highest.ramps.red.colors(100, "hex"),
+          orange: colorScheme.highest.ramps.orange.colors(100, "hex"),
+          yellow: colorScheme.highest.ramps.yellow.colors(100, "hex"),
+          green: colorScheme.highest.ramps.green.colors(100, "hex"),
+          cyan: colorScheme.highest.ramps.cyan.colors(100, "hex"),
+          blue: colorScheme.highest.ramps.blue.colors(100, "hex"),
+          violet: colorScheme.highest.ramps.violet.colors(100, "hex"),
+          magenta: colorScheme.highest.ramps.magenta.colors(100, "hex"),
+        }
+      },
+      players: [
+        colorScheme.players["0"],
+        colorScheme.players["1"],
+        colorScheme.players["2"],
+        colorScheme.players["3"],
+        colorScheme.players["4"],
+        colorScheme.players["5"],
+        colorScheme.players["6"],
+        colorScheme.players["7"],
+      ]
+    }
   };
 }

styles/src/styleTree/editor.ts 🔗

@@ -133,8 +133,8 @@ export default function editor(colorScheme: ColorScheme) {
   return {
     textColor: syntax.primary.color,
     background: background(layer),
-    activeLineBackground: elevation.ramps.neutral(0.29).hex(),
-    highlightedLineBackground: elevation.ramps.neutral(0.18).hex(),
+    activeLineBackground: background(layer, "base", "variant"),
+    highlightedLineBackground: background(layer, "base", "variant"),
     codeActions: {
       indicator: foreground(layer, "base", "variant"),
       verticalScale: 0.618

styles/src/themes/common/ramps.ts 🔗

@@ -123,14 +123,14 @@ function evenSamples(min: number, max: number): number[] {
 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),
+    red: resample(ramps.red, samples),
+    orange: resample(ramps.orange, samples),
+    yellow: resample(ramps.yellow, samples),
+    green: resample(ramps.green, samples),
+    cyan: resample(ramps.cyan, samples),
+    blue: resample(ramps.blue, samples),
+    violet: resample(ramps.violet, samples),
+    magenta: resample(ramps.magenta, samples),
   }
 }
 
@@ -140,22 +140,65 @@ function resample(scale: Scale, samples: number[]): Scale {
 }
 
 function elevation(ramps: RampSet, isLight: boolean, shadow?: Shadow): Elevation {
-  let style: Style = {
-    background: ramps.neutral(0.25).hex(),
-    border: ramps.neutral(0.9).hex(),
+  return {
+    ramps,
+
+    bottom: topLayer(ramps, isLight),
+    middle: topLayer(ramps, isLight),
+    top: topLayer(ramps, isLight),
+
+    shadow,
+  };
+}
+
+function topLayer(ramps: RampSet, isLight: boolean): Layer {
+  let defaultStyle: Style = {
+    background: ramps.neutral(0).hex(),
+    border: ramps.neutral(0.7).hex(),
     foreground: ramps.neutral(1).hex(),
   };
 
+  let variantStyle: Style = {
+    background: ramps.neutral(0.2).hex(),
+    border: ramps.neutral(0.6).hex(),
+    foreground: ramps.neutral(0.8).hex(),
+  };
+
+
+  let hoveredStyle: Style = {
+    background: ramps.neutral(0.4).hex(),
+    border: ramps.neutral(1.0).hex(),
+    foreground: ramps.neutral(0.9).hex(),
+  };
+
+  let pressedStyle: Style = {
+    background: ramps.neutral(0.55).hex(),
+    border: ramps.neutral(0.9).hex(),
+    foreground: ramps.neutral(0.9).hex(),
+  };
+
+  let activeStyle: Style = {
+    background: ramps.neutral(0.8).hex(),
+    border: ramps.neutral(0.8).hex(),
+    foreground: ramps.neutral(0.1).hex(),
+  };
+
+  let disabledStyle: Style = {
+    background: ramps.neutral(0.25).hex(),
+    border: ramps.neutral(1).hex(),
+    foreground: ramps.neutral(0.9).hex(),
+  };
+
   let styleSet: StyleSet = {
-    default: style,
-    variant: style,
-    active: style,
-    disabled: style,
-    hovered: style,
-    pressed: style,
+    default: defaultStyle,
+    variant: variantStyle,
+    hovered: hoveredStyle,
+    pressed: pressedStyle,
+    active: activeStyle,
+    disabled: disabledStyle,
   };
 
-  let layer: Layer = {
+  return {
     base: styleSet,
     on: styleSet,
     info: styleSet,
@@ -163,14 +206,4 @@ function elevation(ramps: RampSet, isLight: boolean, shadow?: Shadow): Elevation
     warning: styleSet,
     negative: styleSet
   };
-
-  return {
-    ramps,
-
-    bottom: layer,
-    middle: layer,
-    top: layer,
-
-    shadow,
-  };
 }