we getting there...

This commit is contained in:
Djkáťo 2024-06-17 22:17:57 +02:00
parent 9011c9f34d
commit 305a58e8b5
45 changed files with 760 additions and 85 deletions

View file

@ -0,0 +1,12 @@
{
"lspconfig": {
"rust_analyzer": {
"rust-analyzer.cargo.features": "all",
"rust-analyzer.rustfmt.overrideCommand": [
"leptosfmt",
"--stdin",
"--rustfmt"
]
}
}
}

View file

@ -18,6 +18,7 @@ crate-type = ["cdylib", "rlib"]
axum = { workspace = true, optional = true }
console_error_panic_hook = { workspace = true }
leptos = { workspace = true, features = ["nightly"] }
anyhow = { workspace = true, optional = true }
leptos_axum = { workspace = true, optional = true }
leptos_meta = { workspace = true, features = ["nightly"] }
leptos_router = { workspace = true, features = ["nightly"] }
@ -29,13 +30,20 @@ tower = { workspace = true, optional = true }
tower-http = { workspace = true, features = ["fs"], optional = true }
wasm-bindgen = { workspace = true }
tracing = { workspace = true, optional = true }
tracing-subscriber = { workspace = true, optional = true }
thiserror = { workspace = true }
http = { workspace = true }
pulldown-cmark = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
saleor-app-sdk = { workspace = true }
saleor-app-sdk = { workspace = true, features = ["bridge"], optional = true }
dotenvy = { workspace = true }
envy = { workspace = true }
cynic = { workspace = true, features = ["http-surf"], optional = true }
surf = { workspace = true, optional = true }
[build-dependencies]
cynic-codegen = { workspace = true, optional = true }
[features]
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
@ -45,12 +53,19 @@ ssr = [
"dep:tower",
"dep:tower-http",
"dep:leptos_axum",
"dep:tracing",
"dep:saleor-app-sdk",
"dep:tracing-subscriber",
"dep:anyhow",
"dep:cynic",
"dep:cynic-codegen",
"dep:surf",
"leptos/ssr",
"leptos_meta/ssr",
"leptos_router/ssr",
"dep:tracing",
]
# Defines a size-optimized profile for the WASM bundle in release mode
[profile.wasm-release]
inherits = "release"

11
app-template-ui/build.rs Normal file
View file

@ -0,0 +1,11 @@
#[cfg(feature = "ssr")]
fn main() {
cynic_codegen::register_schema("saleor")
.from_sdl_file("../schema.graphql")
.unwrap()
.as_default()
.unwrap();
}
#[cfg(not(feature = "ssr"))]
fn main() {}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -0,0 +1,5 @@
[toolchain]
channel = "nightly-2024-04-28"
## Toggle to this one for sdk releases
# channel = "stable"
targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]

View file

@ -15,10 +15,10 @@ pub fn App() -> impl IntoView {
provide_meta_context();
view! {
<Stylesheet id="leptos" href="/pkg/saleor-marketplace.css"/>
<Stylesheet id="leptos" href="/pkg/saleor-app-template-ui.css"/>
// sets the document title
<Title text="Saleors Harbour"/>
<Title text="Example UI App template in Rust"/>
// content for this welcome page
<Router fallback=|| {
@ -28,9 +28,18 @@ pub fn App() -> impl IntoView {
}>
<main class="p-4 md:p-8 md:px-16">
<Routes>
<Route path="" view=Home/>
<Route path="/" view=Home/>
</Routes>
</main>
</Router>
}
}
#[cfg(feature = "ssr")]
#[derive(Debug, Clone)]
pub struct AppState {
pub saleor_app: std::sync::Arc<tokio::sync::Mutex<saleor_app_sdk::SaleorApp>>,
pub config: saleor_app_sdk::config::Config,
pub manifest: saleor_app_sdk::manifest::AppManifest,
pub leptos_options: LeptosOptions,
}

View file

@ -1 +1 @@
use leptos::*;

View file

@ -1 +1 @@
pub mod app_editor;

View file

@ -1,7 +1,41 @@
use http::status::StatusCode;
use leptos::*;
#[cfg(feature = "ssr")]
use axum::response::{IntoResponse, Response};
#[cfg(feature = "ssr")]
use http::header::ToStrError;
use http::status::StatusCode;
use thiserror::Error;
/* ERROR STUFF FOR AXUM */
#[cfg(feature = "ssr")]
#[derive(Error, Debug)]
pub enum AxumError {
#[error("Error converting something to string, `{0}`")]
ToStrError(#[from] ToStrError),
#[error("Anyhow function error: {0}")]
Anyhow(#[from] anyhow::Error),
#[error("Request is missing header `{0}`")]
MissingHeader(String),
#[error("Internal server error, `{0}`")]
InternalServerError(String),
}
// Tell axum how to convert `AppError` into a response.
#[cfg(feature = "ssr")]
impl IntoResponse for AxumError {
fn into_response(self) -> Response {
(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Something went wrong: {:?}", self),
)
.into_response()
}
}
/* THIS IS ERROR STUFF FOR LEPTOS */
#[derive(Clone, Debug, Error)]
pub enum AppError {
#[error("Not Found")]

View file

@ -2,10 +2,11 @@ pub mod app;
pub mod error_template;
#[cfg(feature = "ssr")]
pub mod fileserv;
#[cfg(feature = "ssr")]
pub mod queries;
pub mod components;
pub mod routes;
pub mod server;
#[cfg(feature = "hydrate")]
#[wasm_bindgen::prelude::wasm_bindgen]

View file

@ -1,44 +1,133 @@
pub mod server;
pub mod components;
pub mod routes;
#![allow(
non_upper_case_globals,
clippy::large_enum_variant,
clippy::upper_case_acronyms,
dead_code
)]
#![feature(let_chains)]
#[cfg(feature = "ssr")]
pub mod fileserv;
mod fileserv;
#[cfg(feature = "ssr")]
pub mod error_template;
mod queries;
mod app;
mod components;
mod routes;
mod error_template;
#[cfg(feature = "ssr")]
pub mod app;
#[cfg(feature = "ssr")]
#[tokio::main]
async fn main() {
#[cfg(feature = "ssr")]
async fn main() -> Result<(), std::io::Error> {
use axum::Router;
use std::sync::Arc;
use axum::{middleware, routing::{post,get}, Router};
use leptos::*;
use leptos_axum::{generate_route_list, LeptosRoutes};
use crate::app::*;
use crate::fileserv::file_and_error_handler;
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
// For deployment these variables are:
// <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>
// Alternately a file can be specified such as Some("Cargo.toml")
// The file would need to be included with the executable when moved to deployment
use app::*;
use fileserv::file_and_error_handler;
use saleor_app_sdk::middleware::verify_webhook_signature::webhook_signature_verifier;
use tokio::sync::Mutex;
use saleor_app_sdk::{
cargo_info,
config::Config,
manifest::{AppManifestBuilder, AppPermission},
webhooks::{AsyncWebhookEventType, WebhookManifestBuilder},
SaleorApp,
};
use crate::routes::api::{manifest::manifest, register::register, webhooks::webhooks};
//Leptos stuff
let conf = get_configuration(None).await.unwrap();
let leptos_options = conf.leptos_options;
let addr = leptos_options.site_addr;
let routes = generate_route_list(App);
// build our application with a route
let app = Router::new()
// Saleor stuff
let config = Config::load().unwrap();
trace_to_std(&config).unwrap();
let saleor_app = SaleorApp::new(&config).unwrap();
let app_manifest = AppManifestBuilder::new(&config, cargo_info!())
.add_webhook(
WebhookManifestBuilder::new(&config)
.set_query(
r#"
subscription QueryProductsChanged {
event {
... on ProductUpdated {
product {
... BaseProduct
}
}
... on ProductCreated {
product {
... BaseProduct
}
}
... on ProductDeleted {
product {
... BaseProduct
}
}
}
}
fragment BaseProduct on Product {
id
slug
name
category {
slug
}
}
"#,
)
.add_async_events(vec![
AsyncWebhookEventType::ProductCreated,
AsyncWebhookEventType::ProductUpdated,
AsyncWebhookEventType::ProductDeleted,
])
.build(),
)
.add_permission(AppPermission::ManageProducts)
.build();
let app_state = AppState{
manifest: app_manifest,
config: config.clone(),
saleor_app: Arc::new(Mutex::new(saleor_app)),
leptos_options,
};
let state_1 = app_state.clone();
let app =
Router::new()
.layer(middleware::from_fn(webhook_signature_verifier))
.route("/api/webhooks", post(webhooks))
.route("/api/register", post(register))
.route("/api/manifest", get(manifest))
// .leptos_routes_with_context(&leptos_options, routes,move || provide_context(state_1.clone()) , App)
.leptos_routes(&leptos_options, routes, App)
.fallback(file_and_error_handler)
.with_state(leptos_options);
.with_state(app_state.clone());
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
logging::log!("listening on http://{}", &addr);
axum::serve(listener, app.into_make_service())
.await
.unwrap();
let listener = tokio::net::TcpListener::bind(
"0.0.0.0:".to_owned()
+ config
.app_api_base_url
.split(':')
.collect::<Vec<_>>()
.get(2)
.unwrap_or(&"3000"),
)
.await?;
tracing::debug!("listening on {}", listener.local_addr()?);
let _= axum::serve(listener, app.into_make_service())
.await;
Ok(())
}
#[cfg(not(feature = "ssr"))]
@ -47,3 +136,26 @@ pub fn main() {
// unless we want this to work with e.g., Trunk for a purely client-side app
// see lib.rs for hydration function instead
}
#[cfg(feature = "ssr")]
use saleor_app_sdk::config::Config;
#[cfg(feature = "ssr")]
pub fn trace_to_std(config: &Config) -> Result<(), envy::Error> {
use tracing::level_filters::LevelFilter;
use tracing_subscriber::EnvFilter;
let filter = EnvFilter::builder()
.with_default_directive(LevelFilter::DEBUG.into())
.from_env().unwrap()
.add_directive(format!("{}={}", env!("CARGO_PKG_NAME"), config.log_level).parse().unwrap());
tracing_subscriber::fmt()
.with_max_level(config.log_level)
.with_env_filter(filter)
.with_target(true)
.compact()
.init();
Ok(())
}

View file

@ -0,0 +1,45 @@
#[cynic::schema("saleor")]
mod schema {}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(graphql_type = "Subscription")]
pub struct QueryProductsChanged {
pub event: Option<Event>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct ProductUpdated {
pub product: Option<Product>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct ProductDeleted {
pub product: Option<Product>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct ProductCreated {
pub product: Option<Product>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct Product {
pub id: cynic::Id,
pub slug: String,
pub name: String,
pub category: Option<Category>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct Category {
pub slug: String,
}
#[derive(cynic::InlineFragments, Debug)]
pub enum Event {
ProductUpdated(ProductUpdated),
ProductCreated(ProductCreated),
ProductDeleted(ProductDeleted),
#[cynic(fallback)]
Unknown,
}

View file

@ -0,0 +1,2 @@
pub mod event_products_updated;
pub mod product_metadata_update;

View file

@ -0,0 +1,75 @@
#[cynic::schema("saleor")]
mod schema {}
#[derive(cynic::QueryVariables, Debug)]
pub struct UpdateProductMetadataVariables<'a> {
pub metadata: Option<Vec<MetadataInput<'a>>>,
pub product_id: &'a cynic::Id,
}
#[derive(cynic::QueryFragment, Debug)]
#[cynic(
graphql_type = "Mutation",
variables = "UpdateProductMetadataVariables"
)]
pub struct UpdateProductMetadata {
#[arguments(id: $product_id, input: { metadata: $metadata })]
pub product_update: Option<ProductUpdate>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct ProductUpdate {
pub errors: Vec<ProductError>,
pub product: Option<Product>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct Product {
pub id: cynic::Id,
pub metadata: Vec<MetadataItem>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct ProductError {
pub field: Option<String>,
pub message: Option<String>,
pub code: ProductErrorCode,
pub attributes: Option<Vec<cynic::Id>>,
pub values: Option<Vec<cynic::Id>>,
}
#[derive(cynic::QueryFragment, Debug)]
pub struct MetadataItem {
pub key: String,
pub value: String,
}
#[derive(cynic::Enum, Clone, Copy, Debug)]
pub enum ProductErrorCode {
AlreadyExists,
AttributeAlreadyAssigned,
AttributeCannotBeAssigned,
AttributeVariantsDisabled,
MediaAlreadyAssigned,
DuplicatedInputItem,
GraphqlError,
Invalid,
InvalidPrice,
ProductWithoutCategory,
NotProductsImage,
NotProductsVariant,
NotFound,
Required,
Unique,
VariantNoDigitalContent,
CannotManageProductWithoutVariant,
ProductNotAssignedToChannel,
UnsupportedMediaProvider,
PreorderVariantCannotBeDeactivated,
}
#[derive(cynic::InputObject, Debug)]
pub struct MetadataInput<'a> {
pub key: &'a str,
pub value: &'a str,
}

View file

@ -0,0 +1,8 @@
use axum::{extract::State, Json};
use saleor_app_sdk::manifest::AppManifest;
use crate::{app::AppState, error_template::AxumError};
pub fn manifest(State(state): State<AppState>) -> Result<Json<AppManifest>, AxumError> {
Ok(Json(state.manifest))
}

View file

@ -0,0 +1,3 @@
pub mod manifest;
pub mod register;
pub mod webhooks;

View file

@ -0,0 +1,41 @@
use anyhow::Context;
use axum::{
extract::Json,
extract::State,
http::{HeaderMap, StatusCode},
};
use saleor_app_sdk::{AuthData, AuthToken};
use tracing::{debug, info};
use crate::{app::AppState, error_template::AxumError};
pub async fn register(
headers: HeaderMap,
State(state): State<AppState>,
Json(auth_token): Json<AuthToken>,
) -> Result<StatusCode, AxumError> {
debug!(
"/api/register:\nsaleor_api_url:{:?}\nauth_token:{:?}",
headers.get("saleor-api-url"),
auth_token
);
if auth_token.auth_token.is_empty() {
return Err(anyhow::anyhow!("missing auth_token").into());
}
let app = state.saleor_app.lock().await;
let saleor_api_url = headers.get("saleor-api-url").context("missing api field")?;
let saleor_api_url = saleor_api_url.to_str()?.to_owned();
let auth_data = AuthData {
jwks: None,
token: auth_token.auth_token,
domain: Some(state.config.app_api_base_url),
app_id: state.manifest.id,
saleor_api_url: saleor_api_url.clone(),
};
app.apl.set(auth_data).await?;
info!("registered app for{:?}", &saleor_api_url);
Ok(StatusCode::OK)
}

View file

@ -0,0 +1,70 @@
use axum::{
extract::{Json, State},
http::{HeaderMap, StatusCode},
};
use cynic::{http::SurfExt, MutationBuilder};
use saleor_app_sdk::{
headers::{SALEOR_API_URL_HEADER,SALEOR_EVENT_HEADER},
webhooks::{
utils::{get_webhook_event_type, EitherWebhookType},
AsyncWebhookEventType,
},
};
use tracing::{debug, info};
use crate::{
app::AppState, error_template::AxumError, queries::{event_products_updated::ProductUpdated, product_metadata_update::{MetadataInput, UpdateProductMetadata, UpdateProductMetadataVariables}} };
pub async fn webhooks(
headers: HeaderMap,
State(state): State<AppState>,
//Will try to convert req body to ProductUpdated type, else returns 400
Json(product): Json<ProductUpdated>,
) -> Result<StatusCode, AxumError> {
debug!("/api/webhooks");
debug!("req: {:?}", product);
debug!("headers: {:?}", headers);
let url = headers
.get(SALEOR_API_URL_HEADER).ok_or(AxumError::MissingHeader(SALEOR_API_URL_HEADER.to_owned()))?;
let event_type = get_webhook_event_type(&headers).map_err(|_|AxumError::MissingHeader(SALEOR_EVENT_HEADER.to_owned()))?;
if let EitherWebhookType::Async(a) = event_type {
match a {
AsyncWebhookEventType::ProductUpdated
| AsyncWebhookEventType::ProductCreated
| AsyncWebhookEventType::ProductDeleted => {
update_product(product, url.to_str()?, state).await?
}
_ => (),
}
}
info!("got webhooks!");
Ok(StatusCode::OK)
}
async fn update_product(
product: ProductUpdated,
saleor_api_url: &str,
state: AppState,
) -> Result<(), anyhow::Error> {
debug!("Product got changed!");
if let Some(product) = product.product {
let operation = UpdateProductMetadata::build(UpdateProductMetadataVariables {
product_id: &product.id,
metadata: Some(vec![MetadataInput {
key: "helloloo",
value: "hiiiihii",
}]),
});
let saleor_app = state.saleor_app.lock().await;
let auth_data = saleor_app.apl.get(saleor_api_url).await?;
let result = surf::post(saleor_api_url)
.header("Authorization", format!("bearer {}", auth_data.token))
.run_graphql(operation)
.await;
debug!("update product result : {:?}", result);
}
Ok(())
}

View file

@ -1 +1,3 @@
#[cfg(feature = "ssr")]
pub mod api;
pub mod home;

View file

@ -1 +0,0 @@
use leptos::*;

View file

@ -1 +0,0 @@
pub mod functions;

View file

@ -5,16 +5,18 @@
@tailwind utilities;
@layer base {
@supports (font-variation-settings: normal) {
body {
@apply bg-brand-white;
font-family: "'Inter var',sans-serif";
}
html {
@apply text-brand-black;
}
::selection {
@apply bg-brand-sunset-400;
background-color: rgba(255, 255, 255, 0.2);
}
body {
@apply font-serif text-default1 bg-default1;
}
h1,
@ -23,22 +25,22 @@
h4,
h5,
h6 {
@apply font-sans my-4 text-brand-sea-300 text-2xl;
@apply my-4 text-2xl text-default1;
}
h2 {
@apply text-brand-sea-300 text-xl;
@apply text-xl;
}
h3,
h4,
h5,
h6 {
@apply text-brand-sea-500 text-base;
@apply text-base;
}
p {
@apply font-serif my-2 text-base;
@apply my-2 text-base;
}
input:disabled,
@ -66,13 +68,202 @@
}
}
@layer components {
.header-gradient {
background: theme("colors.brand-sea.400");
background: linear-gradient(25deg,
theme("colors.brand-sunset.500") 0%,
theme("colors.brand-sea.300") 45%,
theme("colors.brand-sea.300") 55%,
theme("colors.brand-sunset.500") 100%);
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-Thin.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 100;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-ThinItalic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 100;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-ExtraLight.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 200;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-ExtraLightItalic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 200;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-Light.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 300;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-LightItalic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 300;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-Regular.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 400;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-Italic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 400;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-Medium.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 500;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-MediumItalic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 500;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-SemiBold.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 600;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-SemiBoldItalic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 600;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-Bold.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 700;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-BoldItalic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 700;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-ExtraBold.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 800;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-ExtraBoldItalic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 800;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-Black.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 900;
}
@font-face {
font-family: "Inter";
src:
local("Inter"),
url("/fonts/Inter-BlackItalic.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 900;
}
@font-face {
font-family: "Inter var";
src:
local("Inter"),
url("/fonts/Inter-roman.var.woff2") format("woff2");
font-display: swap;
font-style: normal;
font-weight: 100 900;
}
@font-face {
font-family: "Inter var";
src:
local("Inter"),
url("/fonts/Inter-italic.var.woff2") format("woff2");
font-display: swap;
font-style: italic;
font-weight: 100 900;
}

View file

@ -1,3 +1,4 @@
/** STYLES TAKEN PARTIALLY FROM SALEORS MACAW-UI**/
/** @type {import('tailwindcss').Config} */
module.exports = {
content: {
@ -5,8 +6,7 @@ module.exports = {
},
theme: {
fontFamily: {
sans: ["Space Grotesk", "sans-serif"],
serif: ["PT Serif", "serif"],
serif: ["Inter", "sans-serif"],
},
fontSize: {
xs: "0.75rem",
@ -26,30 +26,71 @@ module.exports = {
max: "999999px",
},
extend: {
backgroundColor: {
accent1: "hsla(215, 100%, 62%, 1)",
accent1Hovered: "hsla(215, 100%, 51%, 0.16)",
accent1Pressed: "hsla(215, 100%, 51%, 0.32)",
buttonCriticalDisabled: "hsla(204, 16%, 94%, 1)",
buttonCriticalPrimary: "hsla(11, 100%, 56%, 1)",
buttonCriticalPrimaryFocused: "hsla(11, 100%, 42%, 1)",
buttonCriticalPrimaryHovered: "hsla(11, 100%, 42%, 1)",
buttonCriticalPrimaryPressed: "hsla(11, 100%, 29%, 1)",
buttonDefaultDisabled: "hsla(211, 32%, 21%, 1)",
buttonDefaultPrimary: "hsla(0, 0%, 100%, 1)",
buttonDefaultPrimaryFocused: "hsla(210, 24%, 86%, 1)",
buttonDefaultPrimaryHovered: "hsla(211, 24%, 86%, 1)",
buttonDefaultPrimaryPressed: "hsla(211, 16%, 68%, 1)",
buttonDefaultSecondary: "hsla(232, 17%, 18%, 1)",
buttonDefaultSecondaryFocused: "hsla(211, 32%, 19%, 1)",
buttonDefaultSecondaryHovered: "hsla(211, 32%, 19%, 1)",
buttonDefaultSecondaryPressed: "hsla(211, 24%, 26%, 1)",
buttonDefaultTertiary: "hsla(180, 4%, 15%, 0)",
buttonDefaultTertiaryFocused: "hsla(0, 0%, 100%, 0.06)",
buttonDefaultTertiaryHovered: "hsla(0, 0%, 100%, 0.06)",
buttonDefaultTertiaryPressed: "hsla(0, 0%, 100%, 0.12)",
critical1: "hsla(11, 100%, 96%, 1)",
critical1Focused: "hsla(11, 100%, 46%, 0.2)",
critical1Hovered: "hsla(11, 100%, 46%, 0.2)",
critical1Pressed: "hsla(11, 100%, 46%, 0.32)",
critical2: "hsla(11, 100%, 56%, 1)",
default1: "hsla(232, 17%, 18%, 1)",
default1Focused: "hsla(0, 0%, 100%, 0.06)",
default1Hovered: "hsla(0, 0%, 100%, 0.06)",
default1Pressed: "hsla(0, 0%, 100%, 0.12)",
default2: "hsla(231, 17%, 16%, 1)",
default3: "hsla(211, 42%, 12%, 1)",
defaultDisabled: "hsla(211, 32%, 21%, 1)",
info1: "hsla(215, 100%, 62%, 1)",
success1: "hsla(173, 100%, 32%, 1)",
warning1: "hsla(42, 100%, 84%, 1)",
},
borderColor: {
accent1: "hsla(215, 100%, 39%, 1)",
critical1: "hsla(11, 100%, 35%, 1)",
default1: "hsla(210, 32%, 25%, 1)",
default1Focused: "hsla(212, 24%, 32%, 1)",
default1Hovered: "hsla(210, 32%, 25%, 1)",
defaultDisabled: "hsla(231, 18%, 23%, 1)",
default2: "hsla(211, 21%, 39%, 1)",
info1: "hsla(210, 32%, 25%, 1)",
success1: "hsl(173, 79%, 62%, 1)",
warning1: "hsla(36, 44%, 50%, 1)",
},
colors: {
"brand-sea": {
100: "#C5ECE0",
200: "#17C3B2",
300: "#1DA0A8",
400: "#227C9D",
500: "#094074",
},
"brand-sunset": {
100: "#FEF9EF",
200: "#FFEED1",
300: "#FFE2B3",
400: "#FFD795",
500: "#FFCB77",
},
"brand-red": {
100: "#FED6D0",
200: "#FEB3B1",
300: "#FE9092",
400: "#FE7F83",
500: "#FE6D73",
},
"brand-black": "#161a1e",
"brand-white": "#fbfbfb",
accent1: "hsla(215, 100%, 83%, 1)",
buttonCriticalDisabled: "hsla(212, 14%, 67%, 1)",
buttonCriticalPrimary: "hsla(0, 0%, 100%, 1)",
buttonDefaultPrimary: "hsla(212, 44%, 13%, 1)",
buttonDefaultSecondary: "hsla(0, 0%, 100%, 1)",
buttonDefaultTertiary: "hsla(0, 0%, 100%, 1)",
critical1: "hsla(11, 100%, 82%, 1)",
critical2: "hsla(11, 100%, 58%, 1)",
default1: "hsla(0, 0%, 100%, 1)",
default2: "hsla(230, 10%, 53%, 1)",
defaultDisabled: "hsla(212, 19%, 39%, 1)",
info1: "hsla(215, 100%, 83%, 1)",
success1: "hsla(173, 79%, 62%, 1)",
warning1: "hsla(36, 44%, 50%, 1)",
},
},
},