diff --git a/styles/src/themes/common/colorScheme.ts b/styles/src/themes/common/colorScheme.ts index 5cf125ae8f..c32411f23d 100644 --- a/styles/src/themes/common/colorScheme.ts +++ b/styles/src/themes/common/colorScheme.ts @@ -1,6 +1,12 @@ -import { Scale } from "chroma-js" +import { Scale, Color } from "chroma-js" import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax" export { Syntax, ThemeSyntax, SyntaxHighlightStyle } +import { + ThemeConfig, + ThemeAppearance, + ThemeConfigInputColors, +} from "../../themeConfig" +import { getRamps } from "./ramps" export interface ColorScheme { name: string @@ -103,3 +109,183 @@ export interface Style { border: string foreground: string } + +export function createColorScheme(theme: ThemeConfig): ColorScheme { + const { + name, + appearance, + inputColor, + override: { syntax }, + } = theme + + const isLight = appearance === ThemeAppearance.Light + const colorRamps: ThemeConfigInputColors = inputColor + + // Chromajs scales from 0 to 1 flipped if isLight is true + const ramps = getRamps(isLight, colorRamps) + const lowest = lowestLayer(ramps) + const middle = middleLayer(ramps) + const highest = highestLayer(ramps) + + const popoverShadow = { + blur: 4, + color: ramps + .neutral(isLight ? 7 : 0) + .darken() + .alpha(0.2) + .hex(), // TODO used blend previously. Replace with something else + offset: [1, 2], + } + + const modalShadow = { + blur: 16, + color: ramps + .neutral(isLight ? 7 : 0) + .darken() + .alpha(0.2) + .hex(), // TODO used blend previously. Replace with something else + offset: [0, 2], + } + + const players = { + "0": player(ramps.blue), + "1": player(ramps.green), + "2": player(ramps.magenta), + "3": player(ramps.orange), + "4": player(ramps.violet), + "5": player(ramps.cyan), + "6": player(ramps.red), + "7": player(ramps.yellow), + } + + return { + name, + isLight, + + ramps, + + lowest, + middle, + highest, + + popoverShadow, + modalShadow, + + players, + syntax, + } +} + +function player(ramp: Scale): Player { + return { + selection: ramp(0.5).alpha(0.24).hex(), + cursor: ramp(0.5).hex(), + } +} + +function lowestLayer(ramps: RampSet): Layer { + return { + base: buildStyleSet(ramps.neutral, 0.2, 1), + variant: buildStyleSet(ramps.neutral, 0.2, 0.7), + on: buildStyleSet(ramps.neutral, 0.1, 1), + accent: buildStyleSet(ramps.blue, 0.1, 0.5), + positive: buildStyleSet(ramps.green, 0.1, 0.5), + warning: buildStyleSet(ramps.yellow, 0.1, 0.5), + negative: buildStyleSet(ramps.red, 0.1, 0.5), + } +} + +function middleLayer(ramps: RampSet): Layer { + return { + base: buildStyleSet(ramps.neutral, 0.1, 1), + variant: buildStyleSet(ramps.neutral, 0.1, 0.7), + on: buildStyleSet(ramps.neutral, 0, 1), + accent: buildStyleSet(ramps.blue, 0.1, 0.5), + positive: buildStyleSet(ramps.green, 0.1, 0.5), + warning: buildStyleSet(ramps.yellow, 0.1, 0.5), + negative: buildStyleSet(ramps.red, 0.1, 0.5), + } +} + +function highestLayer(ramps: RampSet): Layer { + return { + base: buildStyleSet(ramps.neutral, 0, 1), + variant: buildStyleSet(ramps.neutral, 0, 0.7), + on: buildStyleSet(ramps.neutral, 0.1, 1), + accent: buildStyleSet(ramps.blue, 0.1, 0.5), + positive: buildStyleSet(ramps.green, 0.1, 0.5), + warning: buildStyleSet(ramps.yellow, 0.1, 0.5), + negative: buildStyleSet(ramps.red, 0.1, 0.5), + } +} + +function buildStyleSet( + ramp: Scale, + backgroundBase: number, + foregroundBase: number, + step: number = 0.08 +): StyleSet { + let styleDefinitions = buildStyleDefinition( + backgroundBase, + foregroundBase, + step + ) + + function colorString(indexOrColor: number | Color): string { + if (typeof indexOrColor === "number") { + return ramp(indexOrColor).hex() + } else { + return indexOrColor.hex() + } + } + + function buildStyle(style: Styles): Style { + return { + background: colorString(styleDefinitions.background[style]), + border: colorString(styleDefinitions.border[style]), + foreground: colorString(styleDefinitions.foreground[style]), + } + } + + return { + default: buildStyle("default"), + hovered: buildStyle("hovered"), + pressed: buildStyle("pressed"), + active: buildStyle("active"), + disabled: buildStyle("disabled"), + inverted: buildStyle("inverted"), + } +} + +function buildStyleDefinition( + bgBase: number, + fgBase: number, + step: number = 0.08 +) { + return { + background: { + default: bgBase, + hovered: bgBase + step, + pressed: bgBase + step * 1.5, + active: bgBase + step * 2.2, + disabled: bgBase, + inverted: fgBase + step * 6, + }, + border: { + default: bgBase + step * 1, + hovered: bgBase + step, + pressed: bgBase + step, + active: bgBase + step * 3, + disabled: bgBase + step * 0.5, + inverted: bgBase - step * 3, + }, + foreground: { + default: fgBase, + hovered: fgBase, + pressed: fgBase, + active: fgBase + step * 6, + disabled: bgBase + step * 4, + inverted: bgBase + step * 2, + }, + } +} diff --git a/styles/src/themes/common/ramps.ts b/styles/src/themes/common/ramps.ts index 746b102a84..ef93ee163f 100644 --- a/styles/src/themes/common/ramps.ts +++ b/styles/src/themes/common/ramps.ts @@ -1,14 +1,9 @@ import chroma, { Color, Scale } from "chroma-js" +import { RampSet } from "./colorScheme" import { - ColorScheme, - Layer, - Player, - RampSet, - Style, - Styles, - StyleSet, - ThemeSyntax, -} from "./colorScheme" + ThemeConfigInputColors, + ThemeConfigInputColorsKeys, +} from "../../themeConfig" export function colorRamp(color: Color): Scale { let endColor = color.desaturate(1).brighten(5) @@ -16,200 +11,37 @@ export function colorRamp(color: Color): Scale { return chroma.scale([startColor, color, endColor]).mode("lab") } -export function createColorScheme( - name: string, +/** + * Chromajs mutates the underlying ramp when you call domain. This causes problems because + we now store the ramps object in the theme so that we can pull colors out of them. + So instead of calling domain and storing the result, we have to construct new ramps for each + theme so that we don't modify the passed in ramps. + This combined with an error in the type definitions for chroma js means we have to cast the colors + function to any in order to get the colors back out from the original ramps. + * @param isLight + * @param colorRamps + * @returns + */ +export function getRamps( isLight: boolean, - colorRamps: { [rampName: string]: Scale }, - syntax?: ThemeSyntax -): ColorScheme { - // Chromajs scales from 0 to 1 flipped if isLight is true - let ramps: RampSet = {} as any + colorRamps: ThemeConfigInputColors +): RampSet { + const ramps: RampSet = {} as any + const colorsKeys = Object.keys(colorRamps) as ThemeConfigInputColorsKeys[] - // Chromajs mutates the underlying ramp when you call domain. This causes problems because - // we now store the ramps object in the theme so that we can pull colors out of them. - // So instead of calling domain and storing the result, we have to construct new ramps for each - // theme so that we don't modify the passed in ramps. - // This combined with an error in the type definitions for chroma js means we have to cast the colors - // function to any in order to get the colors back out from the original ramps. if (isLight) { - for (var rampName in colorRamps) { - ;(ramps as any)[rampName] = chroma.scale( + for (const rampName of colorsKeys) { + ramps[rampName] = chroma.scale( colorRamps[rampName].colors(100).reverse() ) } ramps.neutral = chroma.scale(colorRamps.neutral.colors(100).reverse()) } else { - for (var rampName in colorRamps) { - ;(ramps as any)[rampName] = chroma.scale( - colorRamps[rampName].colors(100) - ) + for (const rampName of colorsKeys) { + ramps[rampName] = chroma.scale(colorRamps[rampName].colors(100)) } ramps.neutral = chroma.scale(colorRamps.neutral.colors(100)) } - let lowest = lowestLayer(ramps) - let middle = middleLayer(ramps) - let highest = highestLayer(ramps) - - let popoverShadow = { - blur: 4, - color: ramps - .neutral(isLight ? 7 : 0) - .darken() - .alpha(0.2) - .hex(), // TODO used blend previously. Replace with something else - offset: [1, 2], - } - - let modalShadow = { - blur: 16, - color: ramps - .neutral(isLight ? 7 : 0) - .darken() - .alpha(0.2) - .hex(), // TODO used blend previously. Replace with something else - offset: [0, 2], - } - - let players = { - "0": player(ramps.blue), - "1": player(ramps.green), - "2": player(ramps.magenta), - "3": player(ramps.orange), - "4": player(ramps.violet), - "5": player(ramps.cyan), - "6": player(ramps.red), - "7": player(ramps.yellow), - } - - return { - name, - isLight, - - ramps, - - lowest, - middle, - highest, - - popoverShadow, - modalShadow, - - players, - syntax, - } -} - -function player(ramp: Scale): Player { - return { - selection: ramp(0.5).alpha(0.24).hex(), - cursor: ramp(0.5).hex(), - } -} - -function lowestLayer(ramps: RampSet): Layer { - return { - base: buildStyleSet(ramps.neutral, 0.2, 1), - variant: buildStyleSet(ramps.neutral, 0.2, 0.7), - on: buildStyleSet(ramps.neutral, 0.1, 1), - accent: buildStyleSet(ramps.blue, 0.1, 0.5), - positive: buildStyleSet(ramps.green, 0.1, 0.5), - warning: buildStyleSet(ramps.yellow, 0.1, 0.5), - negative: buildStyleSet(ramps.red, 0.1, 0.5), - } -} - -function middleLayer(ramps: RampSet): Layer { - return { - base: buildStyleSet(ramps.neutral, 0.1, 1), - variant: buildStyleSet(ramps.neutral, 0.1, 0.7), - on: buildStyleSet(ramps.neutral, 0, 1), - accent: buildStyleSet(ramps.blue, 0.1, 0.5), - positive: buildStyleSet(ramps.green, 0.1, 0.5), - warning: buildStyleSet(ramps.yellow, 0.1, 0.5), - negative: buildStyleSet(ramps.red, 0.1, 0.5), - } -} - -function highestLayer(ramps: RampSet): Layer { - return { - base: buildStyleSet(ramps.neutral, 0, 1), - variant: buildStyleSet(ramps.neutral, 0, 0.7), - on: buildStyleSet(ramps.neutral, 0.1, 1), - accent: buildStyleSet(ramps.blue, 0.1, 0.5), - positive: buildStyleSet(ramps.green, 0.1, 0.5), - warning: buildStyleSet(ramps.yellow, 0.1, 0.5), - negative: buildStyleSet(ramps.red, 0.1, 0.5), - } -} - -function buildStyleSet( - ramp: Scale, - backgroundBase: number, - foregroundBase: number, - step: number = 0.08 -): StyleSet { - let styleDefinitions = buildStyleDefinition( - backgroundBase, - foregroundBase, - step - ) - - function colorString(indexOrColor: number | Color): string { - if (typeof indexOrColor === "number") { - return ramp(indexOrColor).hex() - } else { - return indexOrColor.hex() - } - } - - function buildStyle(style: Styles): Style { - return { - background: colorString(styleDefinitions.background[style]), - border: colorString(styleDefinitions.border[style]), - foreground: colorString(styleDefinitions.foreground[style]), - } - } - - return { - default: buildStyle("default"), - hovered: buildStyle("hovered"), - pressed: buildStyle("pressed"), - active: buildStyle("active"), - disabled: buildStyle("disabled"), - inverted: buildStyle("inverted"), - } -} - -function buildStyleDefinition( - bgBase: number, - fgBase: number, - step: number = 0.08 -) { - return { - background: { - default: bgBase, - hovered: bgBase + step, - pressed: bgBase + step * 1.5, - active: bgBase + step * 2.2, - disabled: bgBase, - inverted: fgBase + step * 6, - }, - border: { - default: bgBase + step * 1, - hovered: bgBase + step, - pressed: bgBase + step, - active: bgBase + step * 3, - disabled: bgBase + step * 0.5, - inverted: bgBase - step * 3, - }, - foreground: { - default: fgBase, - hovered: fgBase, - pressed: fgBase, - active: fgBase + step * 6, - disabled: bgBase + step * 4, - inverted: bgBase + step * 2, - }, - } + return ramps }