add el-style, el-layout, el-i18n, el-config, el-secrets: responsive by default, theme-driven, zero breakpoints
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
/// Elevation shadow scale.
|
||||
///
|
||||
/// Shadows communicate visual hierarchy and z-depth. Use the named scale —
|
||||
/// it maps to appropriate platform-native elevation on each backend.
|
||||
|
||||
/// Named shadow/elevation tokens.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Shadow {
|
||||
/// No shadow — flat, on-surface elements.
|
||||
None,
|
||||
/// Subtle drop shadow — slightly elevated cards.
|
||||
Sm,
|
||||
/// Standard card shadow — interactive elements.
|
||||
Md,
|
||||
/// Prominent shadow — dialogs, dropdowns, popovers.
|
||||
Lg,
|
||||
/// Maximum elevation — toasts, context menus, tooltips.
|
||||
Xl,
|
||||
}
|
||||
|
||||
/// A fully-resolved shadow specification.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShadowSpec {
|
||||
/// X offset in dp.
|
||||
pub offset_x: f32,
|
||||
/// Y offset in dp (positive = down).
|
||||
pub offset_y: f32,
|
||||
/// Blur radius in dp.
|
||||
pub blur: f32,
|
||||
/// Spread radius in dp.
|
||||
pub spread: f32,
|
||||
/// Shadow color (RGBA).
|
||||
pub color: (u8, u8, u8, f32),
|
||||
}
|
||||
|
||||
impl ShadowSpec {
|
||||
/// CSS box-shadow string for this spec.
|
||||
pub fn to_css(&self) -> String {
|
||||
format!(
|
||||
"{}px {}px {}px {}px rgba({},{},{},{})",
|
||||
self.offset_x,
|
||||
self.offset_y,
|
||||
self.blur,
|
||||
self.spread,
|
||||
self.color.0,
|
||||
self.color.1,
|
||||
self.color.2,
|
||||
self.color.3,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps Shadow tokens to ShadowSpecs.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ShadowScale {
|
||||
pub sm: ShadowSpec,
|
||||
pub md: ShadowSpec,
|
||||
pub lg: ShadowSpec,
|
||||
pub xl: ShadowSpec,
|
||||
}
|
||||
|
||||
impl ShadowScale {
|
||||
/// Default light-mode shadow scale.
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
sm: ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 1.0,
|
||||
blur: 3.0,
|
||||
spread: 0.0,
|
||||
color: (0, 0, 0, 0.12),
|
||||
},
|
||||
md: ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 4.0,
|
||||
blur: 12.0,
|
||||
spread: -2.0,
|
||||
color: (0, 0, 0, 0.15),
|
||||
},
|
||||
lg: ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 8.0,
|
||||
blur: 24.0,
|
||||
spread: -4.0,
|
||||
color: (0, 0, 0, 0.18),
|
||||
},
|
||||
xl: ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 16.0,
|
||||
blur: 48.0,
|
||||
spread: -8.0,
|
||||
color: (0, 0, 0, 0.22),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Default dark-mode shadow scale (more subtle, less visible on dark bg).
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
sm: ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 1.0,
|
||||
blur: 3.0,
|
||||
spread: 0.0,
|
||||
color: (0, 0, 0, 0.3),
|
||||
},
|
||||
md: ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 4.0,
|
||||
blur: 12.0,
|
||||
spread: -2.0,
|
||||
color: (0, 0, 0, 0.4),
|
||||
},
|
||||
lg: ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 8.0,
|
||||
blur: 24.0,
|
||||
spread: -4.0,
|
||||
color: (0, 0, 0, 0.5),
|
||||
},
|
||||
xl: ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 16.0,
|
||||
blur: 48.0,
|
||||
spread: -8.0,
|
||||
color: (0, 0, 0, 0.6),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a Shadow token to a ShadowSpec.
|
||||
/// Returns None for Shadow::None (no spec needed).
|
||||
pub fn resolve(&self, shadow: &Shadow) -> Option<&ShadowSpec> {
|
||||
match shadow {
|
||||
Shadow::None => None,
|
||||
Shadow::Sm => Some(&self.sm),
|
||||
Shadow::Md => Some(&self.md),
|
||||
Shadow::Lg => Some(&self.lg),
|
||||
Shadow::Xl => Some(&self.xl),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn shadow_none_resolves_to_none() {
|
||||
let scale = ShadowScale::light();
|
||||
assert!(scale.resolve(&Shadow::None).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadow_sm_resolves() {
|
||||
let scale = ShadowScale::light();
|
||||
let spec = scale.resolve(&Shadow::Sm).unwrap();
|
||||
assert!(spec.blur > 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadow_xl_has_larger_blur_than_sm() {
|
||||
let scale = ShadowScale::light();
|
||||
let sm = scale.resolve(&Shadow::Sm).unwrap();
|
||||
let xl = scale.resolve(&Shadow::Xl).unwrap();
|
||||
assert!(xl.blur > sm.blur);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shadow_spec_to_css() {
|
||||
let spec = ShadowSpec {
|
||||
offset_x: 0.0,
|
||||
offset_y: 4.0,
|
||||
blur: 12.0,
|
||||
spread: -2.0,
|
||||
color: (0, 0, 0, 0.15),
|
||||
};
|
||||
let css = spec.to_css();
|
||||
assert!(css.contains("rgba(0,0,0,0.15)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dark_shadows_are_more_opaque() {
|
||||
let light = ShadowScale::light();
|
||||
let dark = ShadowScale::dark();
|
||||
let l_alpha = light.resolve(&Shadow::Md).unwrap().color.3;
|
||||
let d_alpha = dark.resolve(&Shadow::Md).unwrap().color.3;
|
||||
assert!(d_alpha > l_alpha);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user