Skip to content
16 changes: 15 additions & 1 deletion .storybook/preview.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import React from "react";

import ApplicationContainer from "../src/components/Application/ApplicationContainer";
import ApplicationContent from "../src/components/Application/ApplicationContent";

import "./styles.scss";

export const parameters = {
Expand All @@ -17,8 +22,17 @@ export const parameters = {
const preview = {
// Enables auto-generated documentation for all stories
// @see https://storybook.js.org/docs/writing-docs/autodocs
tags: ['autodocs'],
tags: ["autodocs"],
parameters,
decorators: [
(Story) => (
<ApplicationContainer style={{ position: "relative" }}>
<ApplicationContent>
<Story />
</ApplicationContent>
</ApplicationContainer>
),
],
};

export default preview;
11 changes: 11 additions & 0 deletions src/cmem/markdown/Markdown.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import React from "react";
import { Meta, StoryFn } from "@storybook/react";

import { Card, CardContent } from "../../components/Card/index";

import { Markdown } from "./../../../index";

export default {
title: "Cmem/Markdown",
component: Markdown,
argTypes: {},
decorators: [
(Story) => (
<Card>
<CardContent>
<Story />
</CardContent>
</Card>
),
],
} as Meta<typeof Markdown>;

const Template: StoryFn<typeof Markdown> = (args) => <Markdown {...args} />;
Expand Down
143 changes: 142 additions & 1 deletion src/common/scss/_color-functions.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
@error "Need at least 1 color to create color tints.";
}

// we asume that it correct to give only start and end of tint weights
// we assume that it correct to give only start and end of tint weights
// only echo debug message if we have a 1, 3 or 4 color values
@if $count-colors != 2 {
@debug "Got only #{$count-colors} tints: #{$colorset}";
Expand Down Expand Up @@ -103,6 +103,147 @@
}
}

/**
* Reverse alpha compositing ("remove" a background color from a color).
*
* Given an opaque $color that visually results from compositing an unknown
* semi-transparent foreground color over $background, this returns that
* foreground color including the alpha channel, so that
* compositing the result over $background reproduces $color again.
*
* For every channel the minimal alpha is determined that keeps the resulting
* foreground channel inside the valid [0, 255] range; the largest of these
* per-channel alphas is used for all channels. This yields the most
* transparent foreground color possible for the given $background.
*/
@function eccgui-color-subtract-background($color, $background) {
@if meta.type-of($color) != "color" or meta.type-of($background) != "color" {
@error "eccgui-color-subtract-background() expects SASS color values for $color and $background.";
}

$channels: "red" "green" "blue";
$alpha: 0;

// determine the minimal alpha that keeps every foreground channel in range
@each $channel in $channels {
$target: color.channel($color, $channel, $space: rgb);
$base: color.channel($background, $channel, $space: rgb);
$channel-alpha: 0;

@if $target > $base {
// foreground channel goes towards 255, limited by the headroom (255 - base)
$channel-alpha: math.div($target - $base, 255 - $base);
} @else if $target < $base {
// foreground channel goes towards 0, limited by the available range (base)
$channel-alpha: math.div($base - $target, $base);
}

@if $channel-alpha > $alpha {
$alpha: $channel-alpha;
}
}

@if $alpha <= 0 {
// $color equals $background, the foreground is fully transparent
@return transparent;
}

// recover the foreground channels: foreground = base + (target - base) / alpha
$foreground: ();

@each $channel in $channels {
$target: color.channel($color, $channel, $space: rgb);
$base: color.channel($background, $channel, $space: rgb);
$foreground: list.append($foreground, $base + math.div($target - $base, $alpha));
}

@return color.change(
rgb(list.nth($foreground, 1), list.nth($foreground, 2), list.nth($foreground, 3)),
$alpha: $alpha
);
}

/**
* Alpha compositing ("flatten" a semi-transparent color onto a background).
*
* Given a $color that may carry an alpha channel and an opaque $background,
* this composites $color over $background and returns the resulting opaque
* color. It is the inverse of eccgui-color-subtract-background().
*
* Per channel: result = foreground * alpha + background * (1 - alpha).
*/
@function eccgui-color-flatten-foreground($color, $background) {
@if meta.type-of($color) != "color" or meta.type-of($background) != "color" {
@error "eccgui-color-blend-onto-background() expects SASS color values for $color and $background.";
}

$alpha: color.alpha($color);

@if $alpha >= 1 {
// fully opaque foreground, background has no influence
@return color.change($color, $alpha: 1);
}

$channels: "red" "green" "blue";
$blended: ();

@each $channel in $channels {
$foreground: color.channel($color, $channel, $space: rgb);
$base: color.channel($background, $channel, $space: rgb);
$blended: list.append($blended, $foreground * $alpha + $base * (1 - $alpha));
}

@return rgb(list.nth($blended, 1), list.nth($blended, 2), list.nth($blended, 3));
}

/**
* Switch the background color for a color value.
*/
@function eccgui-color-switch-background($color, $bg_old, $bg_new) {
@return eccgui-color-flatten-foreground(eccgui-color-subtract-background($color, $bg_old), $bg_new);
}

/**
* Process a color from a light palette to get used in a dark palette.
*/
@function eccgui-color-darkify($color) {
// very simple process, only invert lightness
$color-dark-simple: color.change($color, $lightness: 100% - color.channel($color, "lightness", $space: hsl));

// read values for lightness and saturation
// use them to improve the final darkified color
$l: color.channel($color-dark-simple, "lightness", $space: hsl);
$s: color.channel($color-dark-simple, "saturation", $space: hsl);

// improve color perception
// for light and saturated colors bring back a bit of the original color
@if $l > 45% and $s > 70% {
$color-dark-simple: color.mix($color, $color-dark-simple, 25%);
$l: color.channel($color-dark-simple, "lightness", $space: hsl);
$s: color.channel($color-dark-simple, "saturation", $space: hsl);
}

// improve lightness
// make dark colors a bit lighter again
@if $l < 50% {
$l: $l + (10% * math.div(50% - $l, 50%));
}

// improve saturation
// decrease it gradually based on lightness and saturation to prevent straining "neon" effects
$s: $s * (1 - 0.1 * math.div($l, 100%) - 0.1 * math.div($s, 100%));

// return improved darkified color, round rgb values
$color-dark-improved: color.change($color-dark-simple, $lightness: $l, $saturation: $s);

@return color.change(
$color-dark-improved,
$red: math.round(color.channel($color-dark-improved, "red")),
$green: math.round(color.channel($color-dark-improved, "green")),
$blue: math.round(color.channel($color-dark-improved, "blue"))
);
}

$debug-rgba-values: "yes";

/**
Expand Down
17 changes: 15 additions & 2 deletions src/components/Application/ApplicationContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,33 @@ export interface ApplicationContainerProps extends React.HTMLAttributes<HTMLDivE
* This need to match with a `dropzone-for` data attribute on available dropzones for dragged elements.
*/
monitorDropzonesFor?: string[];
/**
* Use a light or dark color palette for the GUI.
* On `auto` it depends on the system configuration.
*/
themeMode?: "dark" | "light" | "auto";
}

export const ApplicationContainer = ({
children,
className = "",
monitorDropzonesFor = [],
themeMode = "auto",
...otherDivProps
}: ApplicationContainerProps) => {
const containerRef = React.useRef<any>(null);
const containerRef = React.useRef(null);
useDropzoneMonitor(monitorDropzonesFor);

return (
<OverlaysProvider>
<div ref={containerRef} className={`${eccgui}-application__container ${className}`} {...otherDivProps}>
<div
ref={containerRef}
className={
`${eccgui}-application__container ${eccgui}-palette--${themeMode}` +
(className ? ` ${className}` : "")
}
{...otherDivProps}
>
{children}
</div>
</OverlaysProvider>
Expand Down
30 changes: 27 additions & 3 deletions src/components/Application/_colors.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
@use "sass:map";
@use "sass:list";

:root {
// creating css custom properties from palette colors
@each $palette-group-name, $palette-group-tints in $eccgui-color-palette-light {
/**
* creating css custom properties from palette colors
*/
@mixin eccgui-custom-property-palette($color-palette) {
@each $palette-group-name, $palette-group-tints in $color-palette {
@each $palette-tint-name, $palette-tint-colors in $palette-group-tints {
@for $i from 1 through list.length($palette-tint-colors) {
$css-property-name: #{eccgui-color-name($palette-group-name, $palette-tint-name, ($i * 2 - 1) * 100)};
Expand All @@ -13,6 +15,16 @@
}
}
}
}

:root {
// css custom properties for light palette
@include eccgui-custom-property-palette($eccgui-color-palette-light);

@media (prefers-color-scheme: dark) {
// css custom properties for dark palette
@include eccgui-custom-property-palette($eccgui-color-palette-dark);
}

// set aliases for base colors
--#{$eccgui}-color-primary: #{$eccgui-color-primary};
Expand All @@ -28,3 +40,15 @@
--#{$eccgui}-color-danger-foreground: #{$eccgui-color-danger-text};
--#{$eccgui}-color-danger-background: #{$eccgui-color-danger-background};
}

.#{$eccgui}-palette--light,
html:has(.#{$eccgui}-application__container.#{$eccgui}-palette--light) {
// css custom properties for a forced light palette
@include eccgui-custom-property-palette($eccgui-color-palette-light);
}

.#{$eccgui}-palette--dark,
html:has(.#{$eccgui}-application__container.#{$eccgui}-palette--dark) {
// css custom properties for a forced dark palette
@include eccgui-custom-property-palette($eccgui-color-palette-dark);
}
5 changes: 5 additions & 0 deletions src/components/Application/_container.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
html,
.#{$eccgui}-application__container {
color: $eccgui-color-workspace-text;
background-color: $eccgui-color-workspace-background;
}
2 changes: 2 additions & 0 deletions src/components/Application/_sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ $ui-02: $eccgui-color-workspace-background !default;

.#{$prefix}--side-nav__navigation {
padding: $eccgui-size-block-whitespace calc(0.5 * (#{mini-units(8)} - 30px));
color: eccgui-color-var("identity", "text", "900");
background-color: eccgui-color-var("identity", "background", "100");
transition: none;
}

Expand Down
1 change: 1 addition & 0 deletions src/components/Application/application.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @import 'config';
@import "colors";
@import "container";
@import "header";
@import "toolbar";

Expand Down
14 changes: 13 additions & 1 deletion src/components/Application/stories/Application.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface ApplicationBasicExampleProps {
openUserMenu: boolean;
countNotifications: number;
colorBackgroundHeader?: string;
themeMode?: "light" | "dark" | "auto";
}

function ApplicationBasicExample(args: ApplicationBasicExampleProps) {
Expand All @@ -50,11 +51,15 @@ export default {
colorBackgroundHeader: {
control: { type: "color" },
},
themeMode: {
control: "select",
options: ["auto", "light", "dark"],
},
},
} as Meta<typeof ApplicationBasicExample>;

const TemplateBasicExample: StoryFn<typeof ApplicationBasicExample> = (args) => (
<ApplicationContainer>
<ApplicationContainer themeMode={args.themeMode}>
<ApplicationHeader
aria-label={"Application"}
style={
Expand Down Expand Up @@ -147,3 +152,10 @@ BasicExample.args = {
openUserMenu: false,
countNotifications: 234,
};
BasicExample.decorators = [
(Story) => (
<div style={{ margin: "calc(-1 * var(--eccgui-size-block-whitespace) - 8px)" }}>
<Story />
</div>
),
];
9 changes: 9 additions & 0 deletions src/components/Checkbox/checkbox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ $switch-checked-background-color-disabled: eccgui-color-rgba(
// TODO: set set icon line colors here we need a custom js inject like blueprint uses
color: $eccgui-color-checkbox-checked;
background-image: url("~@carbon/icons/svg/32/checkbox.svg");

@media (prefers-color-scheme: dark) {
filter: invert(1);
}

html:has(.#{$eccgui}-application__container.#{$eccgui}-palette--dark) :not(.#{$eccgui}-palette--light) &,
.#{$eccgui}-palette--dark :not(.#{$eccgui}-palette--light) & {
filter: invert(1);
}
}
input:checked ~ .#{$ns}-control-indicator::before {
background-image: url("~@carbon/icons/svg/32/checkbox--checked.svg");
Expand Down
9 changes: 9 additions & 0 deletions src/components/RadioButton/radiobutton.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@

input ~ .#{$ns}-control-indicator::before {
background-image: url("~@carbon/icons/svg/32/radio-button.svg");

@media (prefers-color-scheme: dark) {
filter: invert(1);
}

html:has(.#{$eccgui}-application__container.#{$eccgui}-palette--dark) :not(.#{$eccgui}-palette--light) &,
.#{$eccgui}-palette--dark :not(.#{$eccgui}-palette--light) & {
filter: invert(1);
}
}
input:checked ~ .#{$ns}-control-indicator::before {
background-image: url("~@carbon/icons/svg/32/radio-button--checked.svg");
Expand Down
Loading
Loading