diff --git a/color-module/src/convert/mod.rs b/color-module/src/convert/mod.rs
index d1cdeae..5eb9cee 100644
--- a/color-module/src/convert/mod.rs
+++ b/color-module/src/convert/mod.rs
@@ -70,6 +70,13 @@ macro_rules! parse_to_oklch {
                 .into_format::<f32>(),
         )
     };
+    ($origin: expr) => {
+        palette::Oklch::from_color(
+            palette::Srgb::from_str($origin)
+                .map_err(|_| crate::errors::ColorError::UnrecogniazedRGB($origin.to_string()))?
+                .into_format::<f32>(),
+        )
+    };
 }
 
 #[macro_export]
diff --git a/color-module/src/schemes/mod.rs b/color-module/src/schemes/mod.rs
index e1ac790..4c02f66 100644
--- a/color-module/src/schemes/mod.rs
+++ b/color-module/src/schemes/mod.rs
@@ -3,6 +3,7 @@ use std::collections::HashMap;
 use material_design_2::MaterialDesign2Scheme;
 use material_design_3::MaterialDesign3Scheme;
 use q_style::{QScheme, SchemeSetting};
+use swatch_style::{SwatchEntry, SwatchSchemeSetting};
 use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
 
 use crate::errors;
@@ -126,3 +127,18 @@ pub fn generate_q_scheme_manually(
     ))
     .map_err(|_| errors::ColorError::UnableToAssembleOutput)?)
 }
+
+#[wasm_bindgen]
+pub fn generate_swatch_scheme(
+    colors: Vec<SwatchEntry>,
+    setting: SwatchSchemeSetting,
+) -> Result<JsValue, errors::ColorError> {
+    let scheme = swatch_style::SwatchScheme::new(colors, setting)?;
+    Ok(serde_wasm_bindgen::to_value(&(
+        scheme.swatches(),
+        scheme.output_css_variables(),
+        scheme.output_scss_variables(),
+        scheme.output_javascript_object(),
+    ))
+    .map_err(|_| errors::ColorError::UnableToAssembleOutput)?)
+}
diff --git a/color-module/src/schemes/swatch_style/mod.rs b/color-module/src/schemes/swatch_style/mod.rs
new file mode 100644
index 0000000..bb2f284
--- /dev/null
+++ b/color-module/src/schemes/swatch_style/mod.rs
@@ -0,0 +1,122 @@
+use palette::FromColor;
+use std::collections::HashMap;
+use std::str::FromStr;
+
+pub use setting::SwatchSchemeSetting;
+use swatch::Swatch;
+use wasm_bindgen::prelude::*;
+
+use crate::{errors, parse_to_oklch};
+
+use super::SchemeExport;
+
+mod setting;
+mod swatch;
+
+#[derive(Debug, Clone)]
+pub struct SwatchScheme {
+    light: HashMap<String, Swatch>,
+    dark: HashMap<String, Swatch>,
+}
+
+#[derive(Debug, Clone)]
+#[wasm_bindgen(getter_with_clone)]
+pub struct SwatchEntry {
+    pub name: String,
+    pub color: String,
+}
+
+impl SwatchScheme {
+    pub fn new(
+        colors: Vec<SwatchEntry>,
+        setting: SwatchSchemeSetting,
+    ) -> Result<Self, errors::ColorError> {
+        let mut light = HashMap::new();
+        let mut dark = HashMap::new();
+
+        for entry in colors {
+            let color = parse_to_oklch!(&entry.color);
+            let darken_color = color * setting.dark_convert;
+            light.insert(entry.name.clone(), Swatch::new(&color, &setting));
+            dark.insert(entry.name, Swatch::new(&darken_color, &setting));
+        }
+
+        Ok(Self { light, dark })
+    }
+
+    pub fn swatches(&self) -> HashMap<String, HashMap<String, Vec<String>>> {
+        let mut light_swatches = HashMap::new();
+        let mut dark_swatches = HashMap::new();
+
+        for (name, swatch) in &self.light {
+            light_swatches.insert(name.clone(), swatch.swtch_hex());
+        }
+        for (name, swatch) in &self.dark {
+            dark_swatches.insert(name.clone(), swatch.swtch_hex());
+        }
+
+        HashMap::from([
+            ("light".to_string(), light_swatches),
+            ("dark".to_string(), dark_swatches),
+        ])
+    }
+}
+
+impl SchemeExport for SwatchScheme {
+    fn output_css_variables(&self) -> String {
+        let mut variables = Vec::new();
+
+        for (name, swatch) in &self.light {
+            variables.extend(swatch.to_css_variables("light", name));
+        }
+        for (name, swatch) in &self.dark {
+            variables.extend(swatch.to_css_variables("dark", name));
+        }
+
+        variables.join("\n")
+    }
+
+    fn output_scss_variables(&self) -> String {
+        let mut variables = Vec::new();
+
+        for (name, swatch) in &self.light {
+            variables.extend(swatch.to_scss_variables("light", name));
+        }
+        for (name, swatch) in &self.dark {
+            variables.extend(swatch.to_scss_variables("dark", name));
+        }
+
+        variables.join("\n")
+    }
+
+    fn output_javascript_object(&self) -> String {
+        let mut object = Vec::new();
+        object.push("{".to_string());
+
+        object.push("  light: {".to_string());
+        for (name, swatch) in &self.light {
+            object.extend(
+                swatch
+                    .to_javascript_fields("light", name)
+                    .iter()
+                    .map(|s| format!("    {}", s)),
+            );
+        }
+        object.push("  },".to_string());
+
+        object.push("  dark: {".to_string());
+        for (name, swatch) in &self.dark {
+            object.extend(
+                swatch
+                    .to_javascript_fields("dark", name)
+                    .iter()
+                    .map(|s| format!("    {}", s)),
+            );
+        }
+        object.push("  },".to_string());
+
+        object.push("}".to_string());
+
+        object.join("\n")
+    }
+}
diff --git a/color-module/src/schemes/swatch_style/setting.rs b/color-module/src/schemes/swatch_style/setting.rs
new file mode 100644
index 0000000..a42dea1
--- /dev/null
+++ b/color-module/src/schemes/swatch_style/setting.rs
@@ -0,0 +1,29 @@
+use serde::Serialize;
+use wasm_bindgen::prelude::wasm_bindgen;
+
+use crate::schemes::q_style::ColorShifting;
+
+#[derive(Debug, Clone, Serialize)]
+#[wasm_bindgen]
+pub struct SwatchSchemeSetting {
+    pub amount: usize,
+    pub min_lightness: f32,
+    pub max_lightness: f32,
+    pub include_primary: bool,
+    pub dark_convert: ColorShifting,
+}
+
+impl Default for SwatchSchemeSetting {
+    fn default() -> Self {
+        Self {
+            amount: 10,
+            min_lightness: 10.0,
+            max_lightness: 90.0,
+            include_primary: false,
+            dark_convert: ColorShifting {
+                chroma: -0.3,
+                lightness: -0.3,
+            },
+        }
+    }
+}
diff --git a/color-module/src/schemes/swatch_style/swatch.rs b/color-module/src/schemes/swatch_style/swatch.rs
new file mode 100644
index 0000000..a445cec
--- /dev/null
+++ b/color-module/src/schemes/swatch_style/swatch.rs
@@ -0,0 +1,133 @@
+use palette::Oklch;
+
+use crate::convert::map_oklch_to_srgb_hex;
+
+use super::setting::SwatchSchemeSetting;
+
+#[derive(Debug, Clone)]
+pub struct Swatch {
+    min_key: f32,
+    max_key: f32,
+    primary_key: Oklch,
+    include_primary: bool,
+    color_amount: usize,
+}
+
+impl Swatch {
+    pub fn new(primary: &Oklch, setting: &SwatchSchemeSetting) -> Self {
+        Self {
+            min_key: primary.l.min(setting.min_lightness),
+            max_key: primary.l.max(setting.max_lightness),
+            primary_key: primary.clone(),
+            include_primary: setting.include_primary,
+            color_amount: setting.amount,
+        }
+    }
+
+    fn find_interval(&self) -> (usize, usize) {
+        if !self.include_primary {
+            return (0, 0);
+        }
+        if self.primary_key.l == self.min_key {
+            return (0, 1);
+        }
+        if self.primary_key.l == self.max_key {
+            return (self.color_amount - 2, self.color_amount - 1);
+        }
+        let step = (self.max_key - self.min_key) / (self.color_amount - 1) as f32;
+        let index = ((self.primary_key.l - self.min_key) / step) as usize;
+
+        (index, index + 1)
+    }
+
+    pub fn swatch(&self) -> Vec<Oklch> {
+        let mut swatch = Vec::new();
+        if self.include_primary {
+            let (_, primary_index) = self.find_interval();
+            if primary_index > 0 {
+                let step = (self.max_key - self.min_key) / primary_index as f32;
+                for i in 0..primary_index {
+                    let lightness = self.min_key + step * i as f32;
+                    swatch.push(Oklch {
+                        l: lightness,
+                        ..self.primary_key
+                    });
+                }
+            }
+            if primary_index < self.color_amount - 1 {
+                let step =
+                    (self.max_key - self.min_key) / (self.color_amount - primary_index) as f32;
+                for i in primary_index..self.color_amount {
+                    let lightness = self.min_key + step * i as f32;
+                    swatch.push(Oklch {
+                        l: lightness,
+                        ..self.primary_key
+                    });
+                }
+            }
+        } else {
+            let step = (self.max_key - self.min_key) / (self.color_amount - 1) as f32;
+            for i in 0..self.color_amount {
+                let lightness = self.min_key + step * i as f32;
+                swatch.push(Oklch {
+                    l: lightness,
+                    ..self.primary_key
+                });
+            }
+        }
+        swatch
+    }
+
+    pub fn swtch_hex(&self) -> Vec<String> {
+        self.swatch().iter().map(map_oklch_to_srgb_hex).collect()
+    }
+
+    pub fn to_css_variables(&self, prefix: &str, name: &str) -> Vec<String> {
+        let mut variables = Vec::new();
+        for (i, color) in self.swatch().iter().enumerate() {
+            variables.push(format!(
+                "--color-{}-{}-{}: #{};",
+                prefix,
+                name,
+                i * 100,
+                map_oklch_to_srgb_hex(color)
+            ));
+        }
+        variables
+    }
+
+    pub fn to_scss_variables(&self, prefix: &str, name: &str) -> Vec<String> {
+        let mut variables = Vec::new();
+        for (i, color) in self.swatch().iter().enumerate() {
+            variables.push(format!(
+                "${}-{}-{}: #{};",
+                prefix,
+                name,
+                i * 100,
+                map_oklch_to_srgb_hex(color)
+            ));
+        }
+        variables
+    }
+
+    pub fn to_javascript_fields(&self, prefix: &str, name: &str) -> Vec<String> {
+        let mut variables = Vec::new();
+        let capitalized_name = name
+            .chars()
+            .next()
+            .unwrap()
+            .to_ascii_uppercase()
+            .to_string()
+            + &name[1..];
+        for (i, color) in self.swatch().iter().enumerate() {
+            variables.push(format!(
+                "{}{}{}: '#{}',",
+                prefix,
+                capitalized_name,
+                i * 100,
+                map_oklch_to_srgb_hex(color)
+            ));
+        }
+        variables
+    }
+}