diff --git a/excalidraw-app/components/GitHubCorner.tsx b/excalidraw-app/components/GitHubCorner.tsx deleted file mode 100644 index 6a4108813c..0000000000 --- a/excalidraw-app/components/GitHubCorner.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { THEME } from "@excalidraw/common"; -import oc from "open-color"; -import React from "react"; - -import type { Theme } from "@excalidraw/element/types"; - -// https://github.com/tholman/github-corners -export const GitHubCorner = React.memo( - ({ theme, dir }: { theme: Theme; dir: string }) => ( - - - - - - - - ), -); diff --git a/packages/common/src/__snapshots__/colors.test.ts.snap b/packages/common/src/__snapshots__/colors.test.ts.snap index 7320bf4d0e..5bb0b4c47d 100644 --- a/packages/common/src/__snapshots__/colors.test.ts.snap +++ b/packages/common/src/__snapshots__/colors.test.ts.snap @@ -1,5 +1,97 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`COLOR_PALETTE > color palette doesn't regress 1`] = ` +{ + "black": "#1e1e1e", + "blue": [ + "#e7f5ff", + "#a5d8ff", + "#4dabf7", + "#228be6", + "#1971c2", + ], + "bronze": [ + "#f8f1ee", + "#eaddd7", + "#d2bab0", + "#a18072", + "#846358", + ], + "cyan": [ + "#e3fafc", + "#99e9f2", + "#3bc9db", + "#15aabf", + "#0c8599", + ], + "grape": [ + "#f8f0fc", + "#eebefa", + "#da77f2", + "#be4bdb", + "#9c36b5", + ], + "gray": [ + "#f8f9fa", + "#e9ecef", + "#ced4da", + "#868e96", + "#343a40", + ], + "green": [ + "#ebfbee", + "#b2f2bb", + "#69db7c", + "#40c057", + "#2f9e44", + ], + "orange": [ + "#fff4e6", + "#ffd8a8", + "#ffa94d", + "#fd7e14", + "#e8590c", + ], + "pink": [ + "#fff0f6", + "#fcc2d7", + "#f783ac", + "#e64980", + "#c2255c", + ], + "red": [ + "#fff5f5", + "#ffc9c9", + "#ff8787", + "#fa5252", + "#e03131", + ], + "teal": [ + "#e6fcf5", + "#96f2d7", + "#38d9a9", + "#12b886", + "#099268", + ], + "transparent": "transparent", + "violet": [ + "#f3f0ff", + "#d0bfff", + "#9775fa", + "#7950f2", + "#6741d9", + ], + "white": "#ffffff", + "yellow": [ + "#fff9db", + "#ffec99", + "#ffd43b", + "#fab005", + "#f08c00", + ], +} +`; + exports[`applyDarkModeFilter > COLOR_PALETTE regression tests > matches snapshot for all palette colors 1`] = ` { "black": "#d3d3d3", diff --git a/packages/common/src/colors.test.ts b/packages/common/src/colors.test.ts index 121fa3c896..4d846c1ef0 100644 --- a/packages/common/src/colors.test.ts +++ b/packages/common/src/colors.test.ts @@ -4,6 +4,12 @@ import { rgbToHex, } from "@excalidraw/common"; +describe("COLOR_PALETTE", () => { + it("color palette doesn't regress", () => { + expect(COLOR_PALETTE).toMatchSnapshot(); + }); +}); + describe("applyDarkModeFilter", () => { describe("basic transformations", () => { it("transforms black to near-white", () => { diff --git a/packages/common/src/colors.ts b/packages/common/src/colors.ts index 662514fa88..54e5c6086a 100644 --- a/packages/common/src/colors.ts +++ b/packages/common/src/colors.ts @@ -1,4 +1,3 @@ -import oc from "open-color"; import tinycolor from "tinycolor2"; import { clamp } from "@excalidraw/math"; @@ -6,18 +5,14 @@ import { degreesToRadians } from "@excalidraw/math"; import type { Degrees } from "@excalidraw/math"; -import type { Merge } from "./utility-types"; - -export { tinycolor }; +// --------------------------------------------------------------------------- +// Dark mode color transformation +// --------------------------------------------------------------------------- // Browser-only cache to avoid memory leaks on server const DARK_MODE_COLORS_CACHE: Map | null = typeof window !== "undefined" ? new Map() : null; -// --------------------------------------------------------------------------- -// Dark mode color transformation -// --------------------------------------------------------------------------- - function cssHueRotate( red: number, green: number, @@ -115,8 +110,8 @@ export const applyDarkModeFilter = (color: string): string => { }; // --------------------------------------------------------------------------- - -export const COLOR_OUTLINE_CONTRAST_THRESHOLD = 240; +// Color palette +// --------------------------------------------------------------------------- // FIXME can't put to utils.ts rn because of circular dependency const pick = , K extends readonly (keyof R)[]>( @@ -131,15 +126,7 @@ const pick = , K extends readonly (keyof R)[]>( }, {} as Pick) as Pick; }; -export type ColorPickerColor = - | Exclude - | "transparent" - | "bronze"; export type ColorTuple = readonly [string, string, string, string, string]; -export type ColorPalette = Merge< - Record, - { black: "#1e1e1e"; white: "#ffffff"; transparent: "transparent" } ->; // used general type instead of specific type (ColorPalette) to support custom colors export type ColorPaletteCustom = { [key: string]: ColorTuple | string }; @@ -152,38 +139,30 @@ export const DEFAULT_CHART_COLOR_INDEX = 4; export const DEFAULT_ELEMENT_STROKE_COLOR_INDEX = 4; export const DEFAULT_ELEMENT_BACKGROUND_COLOR_INDEX = 1; -export const ELEMENTS_PALETTE_SHADE_INDEXES = [0, 2, 4, 6, 8] as const; -export const CANVAS_PALETTE_SHADE_INDEXES = [0, 1, 2, 3, 4] as const; - -export const getSpecificColorShades = ( - color: Exclude< - ColorPickerColor, - "transparent" | "white" | "black" | "bronze" - >, - indexArr: Readonly, -) => { - return indexArr.map((index) => oc[color][index]) as any as ColorTuple; -}; export const COLOR_PALETTE = { transparent: "transparent", black: "#1e1e1e", white: "#ffffff", - // open-colors - gray: getSpecificColorShades("gray", ELEMENTS_PALETTE_SHADE_INDEXES), - red: getSpecificColorShades("red", ELEMENTS_PALETTE_SHADE_INDEXES), - pink: getSpecificColorShades("pink", ELEMENTS_PALETTE_SHADE_INDEXES), - grape: getSpecificColorShades("grape", ELEMENTS_PALETTE_SHADE_INDEXES), - violet: getSpecificColorShades("violet", ELEMENTS_PALETTE_SHADE_INDEXES), - blue: getSpecificColorShades("blue", ELEMENTS_PALETTE_SHADE_INDEXES), - cyan: getSpecificColorShades("cyan", ELEMENTS_PALETTE_SHADE_INDEXES), - teal: getSpecificColorShades("teal", ELEMENTS_PALETTE_SHADE_INDEXES), - green: getSpecificColorShades("green", ELEMENTS_PALETTE_SHADE_INDEXES), - yellow: getSpecificColorShades("yellow", ELEMENTS_PALETTE_SHADE_INDEXES), - orange: getSpecificColorShades("orange", ELEMENTS_PALETTE_SHADE_INDEXES), - // radix bronze shades 3,5,7,9,11 + // open-color from https://github.com/yeun/open-color/blob/master/open-color.js + // corresponds to indexes [0,2,4,6,8] (weights: 50, 200, 400, 600, 800) + gray: ["#f8f9fa", "#e9ecef", "#ced4da", "#868e96", "#343a40"], + red: ["#fff5f5", "#ffc9c9", "#ff8787", "#fa5252", "#e03131"], + pink: ["#fff0f6", "#fcc2d7", "#f783ac", "#e64980", "#c2255c"], + grape: ["#f8f0fc", "#eebefa", "#da77f2", "#be4bdb", "#9c36b5"], + violet: ["#f3f0ff", "#d0bfff", "#9775fa", "#7950f2", "#6741d9"], + blue: ["#e7f5ff", "#a5d8ff", "#4dabf7", "#228be6", "#1971c2"], + cyan: ["#e3fafc", "#99e9f2", "#3bc9db", "#15aabf", "#0c8599"], + teal: ["#e6fcf5", "#96f2d7", "#38d9a9", "#12b886", "#099268"], + green: ["#ebfbee", "#b2f2bb", "#69db7c", "#40c057", "#2f9e44"], + yellow: ["#fff9db", "#ffec99", "#ffd43b", "#fab005", "#f08c00"], + orange: ["#fff4e6", "#ffd8a8", "#ffa94d", "#fd7e14", "#e8590c"], + // radix bronze shades [3,5,7,9,11] bronze: ["#f8f1ee", "#eaddd7", "#d2bab0", "#a18072", "#846358"], -} as ColorPalette; +} as const; + +export type ColorPalette = typeof COLOR_PALETTE; +export type ColorPickerColor = keyof typeof COLOR_PALETTE; const COMMON_ELEMENT_SHADES = pick(COLOR_PALETTE, [ "cyan", @@ -198,7 +177,6 @@ const COMMON_ELEMENT_SHADES = pick(COLOR_PALETTE, [ "red", ]); -// ----------------------------------------------------------------------------- // quick picks defaults // ----------------------------------------------------------------------------- @@ -233,7 +211,6 @@ export const DEFAULT_CANVAS_BACKGROUND_PICKS = [ "#fdf8f6", ] as ColorTuple; -// ----------------------------------------------------------------------------- // palette defaults // ----------------------------------------------------------------------------- @@ -259,8 +236,7 @@ export const DEFAULT_ELEMENT_BACKGROUND_COLOR_PALETTE = { ...COMMON_ELEMENT_SHADES, } as const; -// ----------------------------------------------------------------------------- -// helpers +// color palette helpers // ----------------------------------------------------------------------------- // !!!MUST BE WITHOUT GRAY, TRANSPARENT AND BLACK!!! @@ -281,6 +257,10 @@ export const getAllColorsSpecificShade = (index: 0 | 1 | 2 | 3 | 4) => COLOR_PALETTE.red[index], ] as const; +// ----------------------------------------------------------------------------- +// other helpers +// ----------------------------------------------------------------------------- + export const rgbToHex = (r: number, g: number, b: number, a?: number) => { // (1 << 24) adds 0x1000000 to ensure the hex string is always 7 chars, // then slice(1) removes the leading "1" to get exactly 6 hex digits @@ -299,4 +279,78 @@ export const rgbToHex = (r: number, g: number, b: number, a?: number) => { return hex6; }; +/** + * @returns #RRGGBB or #RRGGBBAA based on color containing non-opaque alpha, + * null if not valid color + */ +export const colorToHex = (color: string): string | null => { + const tc = tinycolor(color); + if (!tc.isValid()) { + return null; + } + const { r, g, b, a } = tc.toRgb(); + return rgbToHex(r, g, b, a); +}; + +export const isTransparent = (color: string) => { + return tinycolor(color).getAlpha() === 0; +}; + // ----------------------------------------------------------------------------- +// color contract helpers +// ----------------------------------------------------------------------------- + +export const COLOR_OUTLINE_CONTRAST_THRESHOLD = 240; + +const calculateContrast = (r: number, g: number, b: number): number => { + const yiq = (r * 299 + g * 587 + b * 114) / 1000; + return yiq; +}; + +// YIQ algo, inspiration from https://stackoverflow.com/a/11868398 +export const isColorDark = (color: string, threshold = 160): boolean => { + // no color ("") -> assume it default to black + if (!color) { + return true; + } + + if (isTransparent(color)) { + return false; + } + + const tc = tinycolor(color); + if (!tc.isValid()) { + // invalid color -> assume it defaults to black + return true; + } + + const { r, g, b } = tc.toRgb(); + return calculateContrast(r, g, b) < threshold; +}; + +// ----------------------------------------------------------------------------- +// normalization +// ----------------------------------------------------------------------------- + +/** + * tries to keep the input color as-is if it's valid, making minimal adjustments + * (trimming whitespace or adding `#` to hex colors) + */ +export const normalizeInputColor = (color: string): string | null => { + color = color.trim(); + if (isTransparent(color)) { + return color; + } + + const tc = tinycolor(color); + if (tc.isValid()) { + // testing for `#` first fixes a bug on Electron (more specfically, an + // Obsidian popout window), where a hex color without `#` is considered valid + if (tc.getFormat() === "hex" && !color.startsWith("#")) { + return `#${color}`; + } + return color; + } + + return null; +}; diff --git a/packages/common/src/utils.ts b/packages/common/src/utils.ts index 64d380af02..3727e562d3 100644 --- a/packages/common/src/utils.ts +++ b/packages/common/src/utils.ts @@ -10,7 +10,6 @@ import type { Zoom, } from "@excalidraw/excalidraw/types"; -import { tinycolor } from "./colors"; import { DEFAULT_VERSION, ENV, @@ -548,10 +547,6 @@ export const mapFind = ( return undefined; }; -export const isTransparent = (color: string) => { - return tinycolor(color).getAlpha() === 0; -}; - export type ResolvablePromise = Promise & { resolve: [T] extends [undefined] ? (value?: MaybePromise>) => void diff --git a/packages/excalidraw/components/Avatar.scss b/packages/excalidraw/components/Avatar.scss index 6565816e32..323a92188e 100644 --- a/packages/excalidraw/components/Avatar.scss +++ b/packages/excalidraw/components/Avatar.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .Avatar { diff --git a/packages/excalidraw/components/Button.scss b/packages/excalidraw/components/Button.scss index 9abcf59d11..d8cb6a1f6f 100644 --- a/packages/excalidraw/components/Button.scss +++ b/packages/excalidraw/components/Button.scss @@ -1,4 +1,4 @@ -@import "../css/theme"; +@use "../css/theme" as *; .excalidraw { .excalidraw-button { diff --git a/packages/excalidraw/components/ButtonIcon.scss b/packages/excalidraw/components/ButtonIcon.scss index e435b69e45..769a195e2f 100644 --- a/packages/excalidraw/components/ButtonIcon.scss +++ b/packages/excalidraw/components/ButtonIcon.scss @@ -1,4 +1,4 @@ -@import "../css/theme"; +@use "../css/theme" as *; .excalidraw { button.standalone { diff --git a/packages/excalidraw/components/Card.scss b/packages/excalidraw/components/Card.scss index ba0fbc437d..e76d933ae2 100644 --- a/packages/excalidraw/components/Card.scss +++ b/packages/excalidraw/components/Card.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module.scss" as *; .excalidraw { .Card { @@ -19,7 +19,7 @@ padding: 1.4rem; border-radius: 50%; background: var(--card-color); - color: $oc-white; + color: #fff; svg { width: 2.8rem; @@ -46,7 +46,7 @@ background-color: var(--card-color-darkest); } .ToolIcon__label { - color: $oc-white; + color: #fff; } .Spinner { diff --git a/packages/excalidraw/components/Card.tsx b/packages/excalidraw/components/Card.tsx index 2ba5a97a56..a7c38e51e4 100644 --- a/packages/excalidraw/components/Card.tsx +++ b/packages/excalidraw/components/Card.tsx @@ -1,25 +1,35 @@ -import OpenColor from "open-color"; - import "./Card.scss"; +// for open-color see https://github.com/yeun/open-color/blob/master/open-color.scss +const COLOR_MAP = { + primary: { + base: "var(--color-primary)", + darker: "var(--color-primary-darker)", + darkest: "var(--color-primary-darkest)", + }, + lime: { + base: "#74b816", // open-color lime[7] + darker: "#66a80f", // open-color lime[8] + darkest: "#5c940d", // open-color lime[9] + }, + pink: { + base: "#d6336c", // open-color pink[7] + darker: "#c2255c", // open-color pink[8] + darkest: "#a61e4d", // open-color pink[9] + }, +}; + export const Card: React.FC<{ - color: keyof OpenColor | "primary"; + color: "primary" | "lime" | "pink"; children?: React.ReactNode; }> = ({ children, color }) => { return (
{children} diff --git a/packages/excalidraw/components/CheckboxItem.scss b/packages/excalidraw/components/CheckboxItem.scss index 6aaf0aab69..3ad775f227 100644 --- a/packages/excalidraw/components/CheckboxItem.scss +++ b/packages/excalidraw/components/CheckboxItem.scss @@ -1,4 +1,5 @@ -@import "../css/variables.module.scss"; +@use "sass:color"; +@use "../css/variables.module" as *; .excalidraw { .Checkbox { @@ -12,7 +13,7 @@ -webkit-tap-highlight-color: transparent; &:hover:not(.is-checked) .Checkbox-box:not(:focus) { - box-shadow: 0 0 0 2px #{$oc-blue-4}; + box-shadow: 0 0 0 2px #{$color-blue-4}; } &:hover:not(.is-checked) .Checkbox-box:not(:focus) { @@ -24,25 +25,25 @@ &:active { .Checkbox-box { - box-shadow: 0 0 2px 1px inset #{$oc-blue-7} !important; + box-shadow: 0 0 2px 1px inset #{$color-blue-7} !important; } } &:hover { .Checkbox-box { - background-color: fade-out($oc-blue-1, 0.8); + background-color: color.adjust($color-blue-1, $alpha: -0.8); } } &.is-checked { .Checkbox-box { - background-color: #{$oc-blue-1}; + background-color: #{$color-blue-1}; svg { display: block; } } &:hover .Checkbox-box { - background-color: #{$oc-blue-2}; + background-color: #{$color-blue-2}; } } @@ -58,16 +59,16 @@ align-items: center; justify-content: center; - box-shadow: 0 0 0 2px #{$oc-blue-7}; + box-shadow: 0 0 0 2px #{$color-blue-7}; background-color: transparent; border-radius: 4px; - color: #{$oc-blue-7}; + color: #{$color-blue-7}; border: 0; &:focus { - box-shadow: 0 0 0 3px #{$oc-blue-7}; + box-shadow: 0 0 0 3px #{$color-blue-7}; } svg { diff --git a/packages/excalidraw/components/ColorPicker/ColorInput.tsx b/packages/excalidraw/components/ColorPicker/ColorInput.tsx index b433b48578..97704ffc09 100644 --- a/packages/excalidraw/components/ColorPicker/ColorInput.tsx +++ b/packages/excalidraw/components/ColorPicker/ColorInput.tsx @@ -1,9 +1,7 @@ import clsx from "clsx"; import { useCallback, useEffect, useRef, useState } from "react"; -import { isTransparent, KEYS } from "@excalidraw/common"; - -import tinycolor from "tinycolor2"; +import { KEYS, normalizeInputColor } from "@excalidraw/common"; import { getShortcutKey } from "../..//shortcut"; import { useAtom } from "../../editor-jotai"; @@ -16,29 +14,6 @@ import { activeColorPickerSectionAtom } from "./colorPickerUtils"; import type { ColorPickerType } from "./colorPickerUtils"; -/** - * tries to keep the input color as-is if it's valid, making minimal adjustments - * (trimming whitespace or adding `#` to hex colors) - */ -export const normalizeInputColor = (color: string): string | null => { - color = color.trim(); - if (isTransparent(color)) { - return color; - } - - const tc = tinycolor(color); - if (tc.isValid()) { - // testing for `#` first fixes a bug on Electron (more specfically, an - // Obsidian popout window), where a hex color without `#` is considered valid - if (tc.getFormat() === "hex" && !color.startsWith("#")) { - return `#${color}`; - } - return color; - } - - return null; -}; - export const ColorInput = ({ color, onChange, diff --git a/packages/excalidraw/components/ColorPicker/ColorPicker.scss b/packages/excalidraw/components/ColorPicker/ColorPicker.scss index 658a75dad7..e1bacfca9a 100644 --- a/packages/excalidraw/components/ColorPicker/ColorPicker.scss +++ b/packages/excalidraw/components/ColorPicker/ColorPicker.scss @@ -1,4 +1,5 @@ -@import "../../css/variables.module.scss"; +@use "sass:color"; +@use "../../css/variables.module" as *; .excalidraw { .focus-visible-none { @@ -185,8 +186,8 @@ .color-picker { background: var(--popup-bg-color); - border: 0 solid transparentize($oc-white, 0.75); - box-shadow: transparentize($oc-black, 0.75) 0 1px 4px; + border: 0 solid color.adjust(#fff, $alpha: -0.75); + box-shadow: color.adjust(#000, $alpha: -0.75) 0 1px 4px; border-radius: 4px; position: absolute; @@ -243,7 +244,7 @@ } .color-picker-triangle-shadow { - border-color: transparent transparent transparentize($oc-black, 0.9); + border-color: transparent transparent color.adjust(#000, $alpha: -0.9); :root[dir="ltr"] & { left: -14px; @@ -280,7 +281,7 @@ padding: 0.25rem; &-title { - color: $oc-gray-6; + color: $color-gray-6; font-size: 12px; padding: 0 0.25rem; } @@ -319,7 +320,7 @@ .color-picker-transparent { border-radius: 4px; - box-shadow: transparentize($oc-black, 0.9) 0 0 0 1px inset; + box-shadow: color.adjust(#000, $alpha: -0.9) 0 0 0 1px inset; position: absolute; top: 0; right: 0; @@ -473,7 +474,7 @@ } .color-picker-type-elementBackground .color-picker-keybinding { - color: $oc-white; + color: #fff; } .color-picker-swatch[aria-label="transparent"] .color-picker-keybinding { @@ -486,10 +487,10 @@ &.theme--dark { .color-picker-type-elementBackground .color-picker-keybinding { - color: $oc-black; + color: #000; } .color-picker-swatch[aria-label="transparent"] .color-picker-keybinding { - color: $oc-black; + color: #000; } } } diff --git a/packages/excalidraw/components/ColorPicker/ColorPicker.tsx b/packages/excalidraw/components/ColorPicker/ColorPicker.tsx index ffbffce962..5de89f7590 100644 --- a/packages/excalidraw/components/ColorPicker/ColorPicker.tsx +++ b/packages/excalidraw/components/ColorPicker/ColorPicker.tsx @@ -5,6 +5,7 @@ import { useRef, useEffect } from "react"; import { COLOR_OUTLINE_CONTRAST_THRESHOLD, COLOR_PALETTE, + isColorDark, isWritableElement, } from "@excalidraw/common"; @@ -29,7 +30,7 @@ import { ColorInput } from "./ColorInput"; import { Picker } from "./Picker"; import PickerHeading from "./PickerHeading"; import { TopPicks } from "./TopPicks"; -import { activeColorPickerSectionAtom, isColorDark } from "./colorPickerUtils"; +import { activeColorPickerSectionAtom } from "./colorPickerUtils"; import "./ColorPicker.scss"; diff --git a/packages/excalidraw/components/ColorPicker/HotkeyLabel.tsx b/packages/excalidraw/components/ColorPicker/HotkeyLabel.tsx index 898a289703..d164c4119f 100644 --- a/packages/excalidraw/components/ColorPicker/HotkeyLabel.tsx +++ b/packages/excalidraw/components/ColorPicker/HotkeyLabel.tsx @@ -1,6 +1,5 @@ import React from "react"; - -import { isColorDark } from "./colorPickerUtils"; +import { isColorDark } from "@excalidraw/common"; interface HotkeyLabelProps { color: string; diff --git a/packages/excalidraw/components/ColorPicker/TopPicks.tsx b/packages/excalidraw/components/ColorPicker/TopPicks.tsx index 6a00b18178..5b5070a5b6 100644 --- a/packages/excalidraw/components/ColorPicker/TopPicks.tsx +++ b/packages/excalidraw/components/ColorPicker/TopPicks.tsx @@ -5,10 +5,9 @@ import { DEFAULT_CANVAS_BACKGROUND_PICKS, DEFAULT_ELEMENT_BACKGROUND_PICKS, DEFAULT_ELEMENT_STROKE_PICKS, + isColorDark, } from "@excalidraw/common"; -import { isColorDark } from "./colorPickerUtils"; - import type { ColorPickerType } from "./colorPickerUtils"; interface TopPicksProps { diff --git a/packages/excalidraw/components/ColorPicker/colorPickerUtils.ts b/packages/excalidraw/components/ColorPicker/colorPickerUtils.ts index 3bf86d85a3..fee8f8cfc2 100644 --- a/packages/excalidraw/components/ColorPicker/colorPickerUtils.ts +++ b/packages/excalidraw/components/ColorPicker/colorPickerUtils.ts @@ -1,8 +1,4 @@ -import { - isTransparent, - MAX_CUSTOM_COLORS_USED_IN_CANVAS, - tinycolor, -} from "@excalidraw/common"; +import { MAX_CUSTOM_COLORS_USED_IN_CANVAS } from "@excalidraw/common"; import type { ExcalidrawElement } from "@excalidraw/element/types"; @@ -100,32 +96,6 @@ export type ActiveColorPickerSectionAtomType = export const activeColorPickerSectionAtom = atom(null); -const calculateContrast = (r: number, g: number, b: number): number => { - const yiq = (r * 299 + g * 587 + b * 114) / 1000; - return yiq; -}; - -// YIQ algo, inspiration from https://stackoverflow.com/a/11868398 -export const isColorDark = (color: string, threshold = 160): boolean => { - // no color ("") -> assume it default to black - if (!color) { - return true; - } - - if (isTransparent(color)) { - return false; - } - - const tc = tinycolor(color); - if (!tc.isValid()) { - // invalid color -> assume it defaults to black - return true; - } - - const { r, g, b } = tc.toRgb(); - return calculateContrast(r, g, b) < threshold; -}; - export type ColorPickerType = | "canvasBackground" | "elementBackground" diff --git a/packages/excalidraw/components/CommandPalette/CommandPalette.scss b/packages/excalidraw/components/CommandPalette/CommandPalette.scss index 0a02c23b04..3706244877 100644 --- a/packages/excalidraw/components/CommandPalette/CommandPalette.scss +++ b/packages/excalidraw/components/CommandPalette/CommandPalette.scss @@ -1,4 +1,4 @@ -@import "../../css/variables.module.scss"; +@use "../../css/variables.module" as *; $verticalBreakpoint: 861px; diff --git a/packages/excalidraw/components/ConfirmDialog.scss b/packages/excalidraw/components/ConfirmDialog.scss index 1fa36fc41d..7dfd956adb 100644 --- a/packages/excalidraw/components/ConfirmDialog.scss +++ b/packages/excalidraw/components/ConfirmDialog.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .confirm-dialog { diff --git a/packages/excalidraw/components/ContextMenu.scss b/packages/excalidraw/components/ContextMenu.scss index 0b370d919b..369d8f3d8e 100644 --- a/packages/excalidraw/components/ContextMenu.scss +++ b/packages/excalidraw/components/ContextMenu.scss @@ -1,4 +1,5 @@ -@import "../css/variables.module.scss"; +@use "sass:color"; +@use "../css/variables.module" as *; .excalidraw { .context-menu-popover { @@ -8,7 +9,7 @@ .context-menu { position: relative; border-radius: 4px; - box-shadow: 0 3px 10px transparentize($oc-black, 0.8); + box-shadow: 0 3px 10px color.adjust(#000, $alpha: -0.8); padding: 0; list-style: none; user-select: none; @@ -49,7 +50,7 @@ &.dangerous { .context-menu-item__label { - color: $oc-red-7; + color: $color-red-7; } } @@ -73,7 +74,7 @@ .context-menu-item__label { color: var(--popup-bg-color); } - background-color: $oc-red-6; + background-color: $color-red-6; } } @@ -97,6 +98,6 @@ .context-menu-item-separator { border: none; - border-top: 1px solid $oc-gray-5; + border-top: 1px solid $color-gray-5; } } diff --git a/packages/excalidraw/components/ConvertElementTypePopup.scss b/packages/excalidraw/components/ConvertElementTypePopup.scss index 414ca9533b..d334c7d368 100644 --- a/packages/excalidraw/components/ConvertElementTypePopup.scss +++ b/packages/excalidraw/components/ConvertElementTypePopup.scss @@ -1,4 +1,4 @@ -@import "../css//variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .ConvertElementTypePopup { diff --git a/packages/excalidraw/components/Dialog.scss b/packages/excalidraw/components/Dialog.scss index 622d304044..4a105077bc 100644 --- a/packages/excalidraw/components/Dialog.scss +++ b/packages/excalidraw/components/Dialog.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .Dialog { diff --git a/packages/excalidraw/components/ElementLinkDialog.scss b/packages/excalidraw/components/ElementLinkDialog.scss index 9923230741..ab501c204d 100644 --- a/packages/excalidraw/components/ElementLinkDialog.scss +++ b/packages/excalidraw/components/ElementLinkDialog.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .ElementLinkDialog { @@ -59,7 +59,7 @@ } .ElementLinkDialog__remove { - color: $oc-red-9; + color: $color-red-9; margin-left: 1rem; .ToolIcon__icon { @@ -68,7 +68,7 @@ } .ToolIcon__icon svg { - color: $oc-red-6; + color: $color-red-6; } } } diff --git a/packages/excalidraw/components/ExportDialog.scss b/packages/excalidraw/components/ExportDialog.scss index d114f25be6..ed3ca6f605 100644 --- a/packages/excalidraw/components/ExportDialog.scss +++ b/packages/excalidraw/components/ExportDialog.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .ExportDialog__preview { @@ -112,7 +112,7 @@ font-family: Cascadia; font-size: 1.8em; - color: $oc-white; + color: #fff; &:hover { background-color: var(--button-color-darker); diff --git a/packages/excalidraw/components/FilledButton.scss b/packages/excalidraw/components/FilledButton.scss index 1f689e9697..44f24f3b0f 100644 --- a/packages/excalidraw/components/FilledButton.scss +++ b/packages/excalidraw/components/FilledButton.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; @keyframes successStatusAnimation { 0% { @@ -24,6 +24,14 @@ background-color: var(--back-color); border-color: var(--border-color); + border-radius: 0.5rem; + border-width: 1px; + border-style: solid; + + font-family: var(--font-family); + + user-select: none; + &:hover { transition: all 150ms ease-out; } @@ -266,14 +274,6 @@ } } - border-radius: 0.5rem; - border-width: 1px; - border-style: solid; - - font-family: var(--font-family); - - user-select: none; - &--size-large { font-weight: 600; font-size: 0.875rem; diff --git a/packages/excalidraw/components/FixedSideContainer.scss b/packages/excalidraw/components/FixedSideContainer.scss index cc99d47d50..15710a551e 100644 --- a/packages/excalidraw/components/FixedSideContainer.scss +++ b/packages/excalidraw/components/FixedSideContainer.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .FixedSideContainer { diff --git a/packages/excalidraw/components/FontPicker/FontPicker.scss b/packages/excalidraw/components/FontPicker/FontPicker.scss index 70859e8091..19492772ab 100644 --- a/packages/excalidraw/components/FontPicker/FontPicker.scss +++ b/packages/excalidraw/components/FontPicker/FontPicker.scss @@ -1,4 +1,4 @@ -@import "../../css/variables.module.scss"; +@use "../../css/variables.module" as *; .excalidraw { .FontPicker__container { diff --git a/packages/excalidraw/components/HelpDialog.scss b/packages/excalidraw/components/HelpDialog.scss index ce4dd441af..dfdd3d0437 100644 --- a/packages/excalidraw/components/HelpDialog.scss +++ b/packages/excalidraw/components/HelpDialog.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .HelpDialog { @@ -60,11 +60,12 @@ &__islands-container { display: grid; + grid-column-gap: 1.5rem; + grid-row-gap: 2rem; + @media screen and (min-width: 1024px) { grid-template-columns: 1fr 1fr; } - grid-column-gap: 1.5rem; - grid-row-gap: 2rem; } @media screen and (min-width: 1024px) { diff --git a/packages/excalidraw/components/HintViewer.scss b/packages/excalidraw/components/HintViewer.scss index bb18bf5a54..b363fe1454 100644 --- a/packages/excalidraw/components/HintViewer.scss +++ b/packages/excalidraw/components/HintViewer.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; // this is loosely based on the longest hint text $wide-viewport-width: 1000px; diff --git a/packages/excalidraw/components/IconPicker.scss b/packages/excalidraw/components/IconPicker.scss index b9b47b39e6..80413e041d 100644 --- a/packages/excalidraw/components/IconPicker.scss +++ b/packages/excalidraw/components/IconPicker.scss @@ -1,10 +1,11 @@ -@import "../css/variables.module.scss"; +@use "sass:color"; +@use "../css/variables.module" as *; .excalidraw { .picker { padding: 0.5rem; background: var(--popup-bg-color); - border: 0 solid transparentize($oc-white, 0.75); + border: 0 solid color.adjust(#fff, $alpha: -0.75); box-shadow: var(--shadow-island); border-radius: 4px; position: absolute; @@ -87,7 +88,7 @@ } .picker-type-elementBackground .picker-keybinding { - color: $oc-white; + color: #fff; } .picker-swatch[aria-label="transparent"] .picker-keybinding { @@ -100,10 +101,10 @@ &.theme--dark { .picker-type-elementBackground .picker-keybinding { - color: $oc-black; + color: #000; } .picker-swatch[aria-label="transparent"] .picker-keybinding { - color: $oc-black; + color: #000; } } } diff --git a/packages/excalidraw/components/ImageExportDialog.scss b/packages/excalidraw/components/ImageExportDialog.scss index ea9e74f805..f20220e943 100644 --- a/packages/excalidraw/components/ImageExportDialog.scss +++ b/packages/excalidraw/components/ImageExportDialog.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { --ImageExportModal-preview-border: #d6d6d6; diff --git a/packages/excalidraw/components/LayerUI.scss b/packages/excalidraw/components/LayerUI.scss index 7ee1469e23..5c76a1ee28 100644 --- a/packages/excalidraw/components/LayerUI.scss +++ b/packages/excalidraw/components/LayerUI.scss @@ -1,5 +1,4 @@ -@import "open-color/open-color"; -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .layer-ui__wrapper.animate { diff --git a/packages/excalidraw/components/LibraryMenu.scss b/packages/excalidraw/components/LibraryMenu.scss index d0d33befd8..6133bb2ecf 100644 --- a/packages/excalidraw/components/LibraryMenu.scss +++ b/packages/excalidraw/components/LibraryMenu.scss @@ -1,4 +1,4 @@ -@import "open-color/open-color"; +@use "../css/variables.module" as *; .excalidraw { .layer-ui__library { @@ -46,15 +46,15 @@ } &-close.ToolIcon_type_button { - background-color: $oc-blue-6; + background-color: $color-blue-6; align-self: flex-end; &:hover { - background-color: $oc-blue-8; + background-color: $color-blue-8; } .ToolIcon__icon { width: auto; font-size: 1rem; - color: $oc-white; + color: #fff; padding: 0 0.5rem; } } @@ -90,7 +90,7 @@ border-radius: var(--border-radius-lg); background-color: var(--color-primary); - color: $oc-white; + color: #fff; text-align: center; white-space: nowrap; text-decoration: none !important; diff --git a/packages/excalidraw/components/LibraryMenuItems.scss b/packages/excalidraw/components/LibraryMenuItems.scss index 3e67774348..a1e5c0da42 100644 --- a/packages/excalidraw/components/LibraryMenuItems.scss +++ b/packages/excalidraw/components/LibraryMenuItems.scss @@ -1,4 +1,4 @@ -@import "open-color/open-color"; +@use "../css/variables.module" as *; .excalidraw { --container-padding-y: 1rem; diff --git a/packages/excalidraw/components/LibraryUnit.scss b/packages/excalidraw/components/LibraryUnit.scss index a0d2161c21..286a00fc5f 100644 --- a/packages/excalidraw/components/LibraryUnit.scss +++ b/packages/excalidraw/components/LibraryUnit.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .library-unit { diff --git a/packages/excalidraw/components/MobileToolBar.scss b/packages/excalidraw/components/MobileToolBar.scss index b936c70ebd..985c676eee 100644 --- a/packages/excalidraw/components/MobileToolBar.scss +++ b/packages/excalidraw/components/MobileToolBar.scss @@ -1,5 +1,4 @@ -@import "open-color/open-color.scss"; -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .mobile-toolbar { diff --git a/packages/excalidraw/components/Modal.scss b/packages/excalidraw/components/Modal.scss index 1a355e2e11..9154079e28 100644 --- a/packages/excalidraw/components/Modal.scss +++ b/packages/excalidraw/components/Modal.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { &.excalidraw-modal-container { diff --git a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.scss b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.scss index 4aad0cbdfd..2ede157e3a 100644 --- a/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.scss +++ b/packages/excalidraw/components/OverwriteConfirm/OverwriteConfirm.scss @@ -1,4 +1,4 @@ -@import "../../css/variables.module.scss"; +@use "../../css/variables.module" as *; .excalidraw { .OverwriteConfirm { diff --git a/packages/excalidraw/components/PasteChartDialog.scss b/packages/excalidraw/components/PasteChartDialog.scss index 855b4ad7cd..3bd21b02be 100644 --- a/packages/excalidraw/components/PasteChartDialog.scss +++ b/packages/excalidraw/components/PasteChartDialog.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .PasteChartDialog { @@ -25,7 +25,7 @@ height: 128px; border-radius: 2px; padding: 1px; - border: 1px solid $oc-gray-4; + border: 1px solid $color-gray-4; display: flex; align-items: center; justify-content: center; @@ -39,7 +39,7 @@ } &:hover { padding: 0; - border: 2px solid $oc-blue-5; + border: 2px solid $color-blue-5; } } } diff --git a/packages/excalidraw/components/PasteChartDialog.tsx b/packages/excalidraw/components/PasteChartDialog.tsx index 2566017ac9..2db632a63b 100644 --- a/packages/excalidraw/components/PasteChartDialog.tsx +++ b/packages/excalidraw/components/PasteChartDialog.tsx @@ -1,4 +1,3 @@ -import oc from "open-color"; import React, { useLayoutEffect, useRef, useState } from "react"; import type { ChartType } from "@excalidraw/element/types"; @@ -49,7 +48,7 @@ const ChartPreviewBtn = (props: { elements, { exportBackground: false, - viewBackgroundColor: oc.white, + viewBackgroundColor: "#fff", }, null, // files { diff --git a/packages/excalidraw/components/PublishLibrary.scss b/packages/excalidraw/components/PublishLibrary.scss index fdcacf1291..c2746f5874 100644 --- a/packages/excalidraw/components/PublishLibrary.scss +++ b/packages/excalidraw/components/PublishLibrary.scss @@ -1,4 +1,4 @@ -@import "../css/variables.module.scss"; +@use "../css/variables.module" as *; .excalidraw { .publish-library { @@ -14,7 +14,7 @@ span { font-weight: 500; font-size: 1rem; - color: $oc-gray-6; + color: $color-gray-6; } input, textarea { @@ -24,7 +24,7 @@ } .required { - color: $oc-red-8; + color: $color-red-8; margin: 0.2rem; } } @@ -48,22 +48,22 @@ } &--confirm.ToolIcon_type_button { - background-color: $oc-blue-6; + background-color: $color-blue-6; &:hover { - background-color: $oc-blue-8; + background-color: $color-blue-8; } } &--cancel.ToolIcon_type_button { - background-color: $oc-gray-5; + background-color: $color-gray-5; &:hover { - background-color: $oc-gray-6; + background-color: $color-gray-6; } } .ToolIcon__icon { - color: $oc-white; + color: #fff; .Spinner { --spinner-color: #fff; svg { @@ -83,7 +83,7 @@ } &-warning { - color: $oc-red-6; + color: $color-red-6; } &-note { @@ -102,14 +102,14 @@ top: 0.3rem; left: 0.3rem; font-size: 0.7rem; - color: $oc-red-7; + color: $color-red-7; background: rgba(255, 255, 255, 0.9); padding: 0.1rem 0.2rem; border-radius: 0.2rem; } &__svg { - background-color: $oc-white; + background-color: #fff; padding: 0.3rem; width: 7.5rem; height: 7.5rem; @@ -121,7 +121,7 @@ } .ToolIcon__icon { - background-color: $oc-white; + background-color: #fff; width: auto; height: auto; margin: 0 0.5rem; @@ -132,7 +132,7 @@ } .required, .error { - color: $oc-red-8; + color: $color-red-8; font-weight: 700; font-size: 1rem; margin: 0.2rem; @@ -152,16 +152,16 @@ margin: 0; } .ToolIcon__icon { - background-color: $oc-red-6; + background-color: $color-red-6; &:hover { - background-color: $oc-red-7; + background-color: $color-red-7; } &:active { - background-color: $oc-red-8; + background-color: $color-red-8; } } svg { - color: $oc-white; + color: #fff; padding: 0.26rem; border-radius: 0.3em; width: 1rem; diff --git a/packages/excalidraw/components/PublishLibrary.tsx b/packages/excalidraw/components/PublishLibrary.tsx index cdc038dac3..0376f812a5 100644 --- a/packages/excalidraw/components/PublishLibrary.tsx +++ b/packages/excalidraw/components/PublishLibrary.tsx @@ -1,5 +1,4 @@ import { exportToCanvas, exportToSvg } from "@excalidraw/utils/export"; -import OpenColor from "open-color"; import { useCallback, useEffect, useRef, useState } from "react"; import { @@ -57,7 +56,7 @@ const generatePreviewImage = async (libraryItems: LibraryItems) => { const ctx = canvas.getContext("2d")!; - ctx.fillStyle = OpenColor.white; + ctx.fillStyle = "#fff"; ctx.fillRect(0, 0, canvas.width, canvas.height); // draw items @@ -87,7 +86,7 @@ const generatePreviewImage = async (libraryItems: LibraryItems) => { // draw item border // ------------------------------------------------------------------------- ctx.lineWidth = BORDER_WIDTH; - ctx.strokeStyle = OpenColor.gray[4]; + ctx.strokeStyle = "#ced4da"; ctx.strokeRect( colOffset + BOX_PADDING / 2, rowOffset + BOX_PADDING / 2, @@ -131,7 +130,7 @@ const SingleLibraryItem = ({ elements: libItem.elements, appState: { ...appState, - viewBackgroundColor: OpenColor.white, + viewBackgroundColor: "#fff", exportBackground: true, }, files: null, @@ -175,7 +174,7 @@ const SingleLibraryItem = ({ }} >
- + {t("publishDialog.itemName")}