diff --git a/.buildkite/scripts/pipelines/pipeline_deploy_new_docs.sh b/.buildkite/scripts/pipelines/pipeline_deploy_new_docs.sh index ebf6a66e8a1..df2598dce0b 100644 --- a/.buildkite/scripts/pipelines/pipeline_deploy_new_docs.sh +++ b/.buildkite/scripts/pipelines/pipeline_deploy_new_docs.sh @@ -33,7 +33,7 @@ analytics_vault="secret/ci/elastic-eui/analytics" export DOCS_BASE_URL="/${bucket_directory}" export DOCS_GOOGLE_TAG_MANAGER_ID="$(retry 5 vault read -field=google_tag_manager_id "${analytics_vault}")" -yarn workspaces foreach -Rpt --from @elastic/eui-website run build +yarn workspace @elastic/eui-website run build:workspaces echo "+++ Configuring environment for website deployment" diff --git a/.buildkite/scripts/pipelines/pipeline_test.sh b/.buildkite/scripts/pipelines/pipeline_test.sh index 8ec1d30c660..220e5313705 100644 --- a/.buildkite/scripts/pipelines/pipeline_test.sh +++ b/.buildkite/scripts/pipelines/pipeline_test.sh @@ -29,53 +29,53 @@ COMMAND="" case $TEST_TYPE in lint) echo "[TASK]: Running linters" - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui lint" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui lint" ;; unit:ts) echo "[TASK]: Running .ts and .js unit tests" - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui test-unit --node-options=--max_old_space_size=2048 --testMatch=non-react" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui test-unit --node-options=--max_old_space_size=2048 --testMatch=non-react" ;; unit:tsx:16) echo "[TASK]: Running Jest .tsx tests against React 16" DOCKER_OPTIONS+=(--env BUILDKITE_ANALYTICS_TOKEN="$(retry 5 vault read -field=jest_token_react16 "${buildkite_analytics_vault}")") - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui test-unit --node-options=--max_old_space_size=2048 --react-version=16 --testMatch=react" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui test-unit --node-options=--max_old_space_size=2048 --react-version=16 --testMatch=react" ;; unit:tsx:17) echo "[TASK]: Running Jest .tsx tests against React 17" DOCKER_OPTIONS+=(--env BUILDKITE_ANALYTICS_TOKEN="$(retry 5 vault read -field=jest_token_react17 "${buildkite_analytics_vault}")") - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui test-unit --node-options=--max_old_space_size=2048 --react-version=17 --testMatch=react" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui test-unit --node-options=--max_old_space_size=2048 --react-version=17 --testMatch=react" ;; unit:tsx) echo "[TASK]: Running Jest .tsx tests against React 18" DOCKER_OPTIONS+=(--env BUILDKITE_ANALYTICS_TOKEN="$(retry 5 vault read -field=jest_token_react18 "${buildkite_analytics_vault}")") - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui test-unit --node-options=--max_old_space_size=2048 --testMatch=react" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui test-unit --node-options=--max_old_space_size=2048 --testMatch=react" ;; cypress:16) echo "[TASK]: Running Cypress tests against React 16" DOCKER_OPTIONS+=(--env BUILDKITE_ANALYTICS_TOKEN="$(retry 5 vault read -field=cypress_token_react16 "${buildkite_analytics_vault}")") - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui cypress install && yarn --cwd packages/eui test-cypress --node-options=--max_old_space_size=2048 --react-version=16" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui cypress install && yarn --cwd packages/eui test-cypress --node-options=--max_old_space_size=2048 --react-version=16" ;; cypress:17) echo "[TASK]: Running Cypress tests against React 17" DOCKER_OPTIONS+=(--env BUILDKITE_ANALYTICS_TOKEN="$(retry 5 vault read -field=cypress_token_react17 "${buildkite_analytics_vault}")") - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui cypress install && yarn --cwd packages/eui test-cypress --node-options=--max_old_space_size=2048 --react-version=17" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui cypress install && yarn --cwd packages/eui test-cypress --node-options=--max_old_space_size=2048 --react-version=17" ;; cypress:18) echo "[TASK]: Running Cypress tests against React 18" DOCKER_OPTIONS+=(--env BUILDKITE_ANALYTICS_TOKEN="$(retry 5 vault read -field=cypress_token_react18 "${buildkite_analytics_vault}")") - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui cypress install && yarn --cwd packages/eui test-cypress --node-options=--max_old_space_size=2048" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui cypress install && yarn --cwd packages/eui test-cypress --node-options=--max_old_space_size=2048" ;; cypress:a11y) echo "[TASK]: Running Cypress accessibility tests against React 18" - COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui cypress install && yarn --cwd packages/eui run test-cypress-a11y --node-options=--max_old_space_size=2048" + COMMAND="/opt/yarn*/bin/yarn --cwd packages/eui && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui cypress install && yarn --cwd packages/eui run test-cypress-a11y --node-options=--max_old_space_size=2048" ;; *) diff --git a/.buildkite/scripts/release/step_build.sh b/.buildkite/scripts/release/step_build.sh index 3a9251b6e28..01d96bf62d8 100755 --- a/.buildkite/scripts/release/step_build.sh +++ b/.buildkite/scripts/release/step_build.sh @@ -10,6 +10,7 @@ echo "+++ :yarn: Installing dependencies" yarn echo "+++ :yarn: Building @elastic/eui" +yarn build:workspaces yarn build echo "+++ :yarn: Built @elastic/eui" diff --git a/packages/docusaurus-theme/package.json b/packages/docusaurus-theme/package.json index 0bdd0f5b122..7f0eeef3a8c 100644 --- a/packages/docusaurus-theme/package.json +++ b/packages/docusaurus-theme/package.json @@ -34,8 +34,9 @@ "@docusaurus/theme-common": "^3.5.2", "@docusaurus/utils-validation": "^3.5.2", "@elastic/datemath": "^5.0.3", - "@elastic/eui": "94.5.0", + "@elastic/eui": "workspace:^", "@elastic/eui-docgen": "workspace:^", + "@elastic/eui-theme-borealis": "workspace:^", "@emotion/css": "^11.11.2", "@emotion/react": "^11.11.4", "@types/react-window": "^1.8.8", diff --git a/packages/docusaurus-theme/src/components/demo/actions_bar/actions_bar.tsx b/packages/docusaurus-theme/src/components/demo/actions_bar/actions_bar.tsx index 14130537e55..95f52bd8efe 100644 --- a/packages/docusaurus-theme/src/components/demo/actions_bar/actions_bar.tsx +++ b/packages/docusaurus-theme/src/components/demo/actions_bar/actions_bar.tsx @@ -28,23 +28,24 @@ const getDemoActionsBarStyles = (euiTheme: UseEuiTheme) => { &:last-child { // border radius should be 1px smaller to work nicely // with the wrapper border width of 1px - border-radius: 0 0 calc(var(--docs-demo-border-radius) - 1px) calc(var(--docs-demo-border-radius) - 1px); + border-radius: 0 0 calc(var(--docs-demo-border-radius) - 1px) + calc(var(--docs-demo-border-radius) - 1px); } `, button: css` - background: var(--eui-background-color-primary-opaque); + background: var(--eui-background-color-primary); border: 1px solid var(--eui-border-color-primary); margin-right: auto; `, }; -} +}; export const DemoActionsBar = ({ isSourceOpen, setSourceOpen, onClickOpenInCodeSandbox, onClickReloadExample, - onClickCopyToClipboard + onClickCopyToClipboard, }: DemoActionsBarProps) => { const styles = useEuiMemoizedStyles(getDemoActionsBarStyles); diff --git a/packages/docusaurus-theme/src/components/navbar_item/index.tsx b/packages/docusaurus-theme/src/components/navbar_item/index.tsx index 130047a50e8..f4bacf65cfd 100644 --- a/packages/docusaurus-theme/src/components/navbar_item/index.tsx +++ b/packages/docusaurus-theme/src/components/navbar_item/index.tsx @@ -91,9 +91,9 @@ export const NavbarItem = (props: Props) => { } = props; const isBrowser = useIsBrowser(); - const { theme } = useContext(AppThemeContext); + const { colorMode } = useContext(AppThemeContext); - const isDarkMode = theme === 'dark'; + const isDarkMode = colorMode === 'dark'; const styles = useEuiMemoizedStyles(getStyles); const cssStyles = [ diff --git a/packages/docusaurus-theme/src/components/theme_context/index.tsx b/packages/docusaurus-theme/src/components/theme_context/index.tsx index d8a394fc8d5..d1fe2208f15 100644 --- a/packages/docusaurus-theme/src/components/theme_context/index.tsx +++ b/packages/docusaurus-theme/src/components/theme_context/index.tsx @@ -5,17 +5,39 @@ import { useState, } from 'react'; import useIsBrowser from '@docusaurus/useIsBrowser'; -import { EUI_THEMES, EuiProvider, EuiThemeColorMode } from '@elastic/eui'; +import { + EuiProvider, + EuiThemeAmsterdam, + EuiThemeColorMode, +} from '@elastic/eui'; +import { EuiThemeBorealis } from '@elastic/eui-theme-borealis'; import { EuiThemeOverrides } from './theme_overrides'; -const EUI_THEME_NAMES = EUI_THEMES.map( - ({ value }) => value -) as EuiThemeColorMode[]; +const EXPERIMENTAL_THEMES = [ + { + text: 'Borealis', + value: EuiThemeBorealis.key, + provider: EuiThemeBorealis, + }, +]; + +export const AVAILABLE_THEMES = [ + { + text: 'Amsterdam', + value: EuiThemeAmsterdam.key, + provider: EuiThemeAmsterdam, + }, + ...EXPERIMENTAL_THEMES, +]; + +const EUI_COLOR_MODES = ['light', 'dark'] as EuiThemeColorMode[]; const defaultState = { - theme: EUI_THEME_NAMES[0] as EuiThemeColorMode, - changeTheme: (themeValue: EuiThemeColorMode) => {}, + colorMode: EUI_COLOR_MODES[0] as EuiThemeColorMode, + changeColorMode: (colorMode: EuiThemeColorMode) => {}, + theme: AVAILABLE_THEMES[0]!, + changeTheme: (themeValue: string) => {}, }; export const AppThemeContext = createContext(defaultState); @@ -24,25 +46,39 @@ export const AppThemeProvider: FunctionComponent = ({ children, }) => { const isBrowser = useIsBrowser(); - const [theme, setTheme] = useState(() => { + const [colorMode, setColorMode] = useState(() => { if (isBrowser) { - return localStorage.getItem('theme') as EuiThemeColorMode ?? defaultState.theme; + return ( + (localStorage.getItem('theme') as EuiThemeColorMode) ?? + defaultState.colorMode + ); } - return defaultState.theme; + return defaultState.colorMode; }); + const [theme, setTheme] = useState(defaultState.theme); + + const handleChangeTheme = (themeValue: string) => { + const themeObj = AVAILABLE_THEMES.find((t) => t.value === themeValue); + + setTheme((currentTheme) => themeObj ?? currentTheme); + }; + return ( {children} diff --git a/packages/docusaurus-theme/src/components/theme_switcher/index.tsx b/packages/docusaurus-theme/src/components/theme_switcher/index.tsx new file mode 100644 index 00000000000..eeb4a1706d3 --- /dev/null +++ b/packages/docusaurus-theme/src/components/theme_switcher/index.tsx @@ -0,0 +1,94 @@ +import { useContext, useEffect, useState } from 'react'; +import { css } from '@emotion/react'; +import { + EuiAvatar, + EuiButtonEmpty, + euiFocusRing, + EuiListGroup, + EuiListGroupItem, + EuiPopover, + useEuiMemoizedStyles, + useEuiTheme, + UseEuiTheme, +} from '@elastic/eui'; + +import { AppThemeContext, AVAILABLE_THEMES } from '../theme_context'; + +const getStyles = (euiThemeContext: UseEuiTheme) => { + const { euiTheme } = euiThemeContext; + + return { + button: css` + padding: 0; + `, + listItem: css` + .euiListGroupItem__button:focus-visible { + // overriding the global "outset" style to ensure the focus style is not cut off + ${euiFocusRing(euiThemeContext, 'inset', { + color: euiTheme.colors.primary, + })}; + } + `, + }; +}; + +export const ThemeSwitcher = () => { + const { euiTheme } = useEuiTheme(); + const [currentTheme, setCurrentTheme] = useState( + AVAILABLE_THEMES[0]?.value ?? '' + ); + const [isPopoverOpen, setPopoverOpen] = useState(false); + const { theme, changeTheme } = useContext(AppThemeContext); + + useEffect(() => { + changeTheme(currentTheme); + }, [currentTheme]); + + const styles = useEuiMemoizedStyles(getStyles); + + const button = ( + setPopoverOpen((isOpen) => !isOpen)} + aria-label={`${theme.text} theme`} + > + + + ); + + return ( + setPopoverOpen(false)} + button={button} + panelPaddingSize="xs" + repositionOnScroll + aria-label="EUI theme list" + > + + {AVAILABLE_THEMES && + AVAILABLE_THEMES.map((theme) => { + const isCurrentTheme = currentTheme === theme.value; + + const handleOnClick = () => { + setCurrentTheme(theme.value); + }; + + return ( + + ); + })} + + + ); +}; diff --git a/packages/docusaurus-theme/src/theme/ColorModeToggle/index.tsx b/packages/docusaurus-theme/src/theme/ColorModeToggle/index.tsx index aaefb2441bc..970e11e0397 100644 --- a/packages/docusaurus-theme/src/theme/ColorModeToggle/index.tsx +++ b/packages/docusaurus-theme/src/theme/ColorModeToggle/index.tsx @@ -17,20 +17,20 @@ function ColorModeToggle({ onChange, ...rest }: WrappedProps): JSX.Element { - const { theme, changeTheme } = useContext(AppThemeContext); + const { colorMode, changeColorMode } = useContext(AppThemeContext); useEffect(() => { - changeTheme(value); + changeColorMode(value); }, []); - const handleOnChange = (themeName: EuiThemeColorMode) => { - changeTheme(themeName); - onChange?.(themeName); + const handleOnChange = (colorMode: EuiThemeColorMode) => { + changeColorMode(colorMode); + onChange?.(colorMode); }; return ( diff --git a/packages/docusaurus-theme/src/theme/EditThisPage/index.tsx b/packages/docusaurus-theme/src/theme/EditThisPage/index.tsx index 15ad94fc05f..717316ca9f6 100644 --- a/packages/docusaurus-theme/src/theme/EditThisPage/index.tsx +++ b/packages/docusaurus-theme/src/theme/EditThisPage/index.tsx @@ -5,7 +5,7 @@ import type { Props } from '@theme-original/EditThisPage'; import { EuiButton, useEuiMemoizedStyles, UseEuiTheme } from '@elastic/eui'; // @ts-ignore - eui only has module declarations for '@elastic/eui/src/themes/amsterdam/global_styling/mixins/button' // but importing from /src results in "Module not found" error -import { euiButtonColor } from '@elastic/eui/lib/themes/amsterdam/global_styling/mixins/button'; +import { euiButtonColor } from '@elastic/eui/lib/global_styling/mixins/_button'; const getStyles = (theme: UseEuiTheme) => { const { euiTheme } = theme; diff --git a/packages/docusaurus-theme/src/theme/Logo/index.tsx b/packages/docusaurus-theme/src/theme/Logo/index.tsx index 3f64a7bd951..ff434592173 100644 --- a/packages/docusaurus-theme/src/theme/Logo/index.tsx +++ b/packages/docusaurus-theme/src/theme/Logo/index.tsx @@ -62,8 +62,8 @@ function LogoThemedImage({ alt: string; imageClassName?: string; }) { - const { theme } = useContext(AppThemeContext); - const isDarkMode = theme === 'dark'; + const { colorMode } = useContext(AppThemeContext); + const isDarkMode = colorMode === 'dark'; const styles = useEuiMemoizedStyles(getStyles); diff --git a/packages/docusaurus-theme/src/theme/Navbar/Content/index.tsx b/packages/docusaurus-theme/src/theme/Navbar/Content/index.tsx index 53b4f771ead..5fcac599372 100644 --- a/packages/docusaurus-theme/src/theme/Navbar/Content/index.tsx +++ b/packages/docusaurus-theme/src/theme/Navbar/Content/index.tsx @@ -15,7 +15,13 @@ import SearchBar from '@theme-original/SearchBar'; import NavbarMobileSidebarToggle from '@theme-original/Navbar/MobileSidebar/Toggle'; import NavbarLogo from '@theme-original/Navbar/Logo'; import NavbarSearch from '@theme-original/Navbar/Search'; -import { euiFocusRing, useEuiMemoizedStyles, UseEuiTheme } from '@elastic/eui'; +import { + euiFocusRing, + euiTextTruncate, + useEuiMemoizedStyles, + UseEuiTheme, + isExperimentalThemeEnabled, +} from '@elastic/eui'; import { euiFormControlText, euiFormVariables, @@ -24,6 +30,7 @@ import { import euiVersions from '@site/static/versions.json'; import { VersionSwitcher } from '../../../components/version_switcher'; +import { ThemeSwitcher } from '../../../components/theme_switcher'; const DOCS_PATH = '/docs'; @@ -67,6 +74,10 @@ const getStyles = (euiThemeContext: UseEuiTheme) => { @media (min-width: 997px) { gap: ${euiTheme.size.l}; } + + .navbar__link { + ${euiTextTruncate()} + } `, navbarItemsRight: css` gap: ${euiTheme.size.s}; @@ -125,6 +136,11 @@ const getStyles = (euiThemeContext: UseEuiTheme) => { display: none; } `, + themeSwitcher: css` + @media (max-width: 996px) { + display: none; + } + `, }; }; @@ -218,6 +234,12 @@ export default function NavbarContent(): JSX.Element { )} + + {isBrowser && isExperimentalThemeEnabled() && ( +
+ +
+ )} } /> diff --git a/packages/docusaurus-theme/src/theme/Navbar/MobileSidebar/Header/index.tsx b/packages/docusaurus-theme/src/theme/Navbar/MobileSidebar/Header/index.tsx index 08791e527a8..0e0daa13ed1 100644 --- a/packages/docusaurus-theme/src/theme/Navbar/MobileSidebar/Header/index.tsx +++ b/packages/docusaurus-theme/src/theme/Navbar/MobileSidebar/Header/index.tsx @@ -9,6 +9,8 @@ import useIsBrowser from '@docusaurus/useIsBrowser'; import euiVersions from '@site/static/versions.json'; import { VersionSwitcher } from '../../../../components/version_switcher'; +import { ThemeSwitcher } from '../../../../components/theme_switcher'; +import { isExperimentalThemeEnabled } from '@elastic/eui/lib/themes/themes'; const getStyles = ({ euiTheme }: UseEuiTheme) => ({ sidebar: css` @@ -64,6 +66,7 @@ export default function NavbarMobileSidebarHeader(): JSX.Element {
{isBrowser && versions && } + {isBrowser && isExperimentalThemeEnabled() && }
diff --git a/packages/docusaurus-theme/src/theme/Root.styles.ts b/packages/docusaurus-theme/src/theme/Root.styles.ts index d80e7dc73d6..6e42126a456 100644 --- a/packages/docusaurus-theme/src/theme/Root.styles.ts +++ b/packages/docusaurus-theme/src/theme/Root.styles.ts @@ -7,11 +7,8 @@ */ import { - euiBorderColor, euiFontSizeFromScale, euiLineHeightFromBaseline, - useEuiBackgroundColor, - useEuiFocusRing, UseEuiTheme, } from '@elastic/eui'; import { css } from '@emotion/react'; @@ -47,77 +44,76 @@ export const getGlobalStyles = (theme: UseEuiTheme) => { const lineHeightXS = '1.33rem'; const lineHeightXXS = euiLineHeightFromBaseline('xxs', euiTheme); + console.log(euiTheme.colors.backgroundBaseSubdued); + return css` - // color theme related variables - :root, - [data-theme='dark']:root { - /* EUI theme variables */ - --eui-background-color-primary: ${useEuiBackgroundColor('primary')}; - --eui-background-color-primary-opaque: ${useEuiBackgroundColor( - 'primary', - { method: 'opaque' } - )}; - --eui-background-color-success: ${useEuiBackgroundColor('success')}; - --eui-background-color-danger: ${useEuiBackgroundColor('danger')}; - --eui-background-color-warning: ${useEuiBackgroundColor('warning')}; - --eui-background-color-accent: ${useEuiBackgroundColor('accent')}; - - --eui-color-danger-text: ${euiTheme.colors.dangerText}; - - /* Docusaurus theme variables */ - --ifm-background-color: ${colors.body}; - --ifm-font-color-base: ${colors.text}; - --ifm-link-color: ${colors.link}; - --ifm-link-hover-color: ${colors.link}; - - --ifm-menu-color: ${euiTheme.colors.text}; - --ifm-menu-color-background-active: ${euiTheme.colors.lightestShade}; - --ifm-menu-color-background-hover: var(--eui-background-color-primary); - - --ifm-pre-background: ${euiTheme.colors.lightestShade}; - } - - :root { - /* EUI theme variables */ - --eui-font-size-base: ${fontBase.fontSize}; - --eui-font-size-xxl: ${fontSizeXXL}; - --eui-font-size-xl: ${fontSizeXL}; - --eui-font-size-l: ${fontSizeL}; - --eui-font-size-m: ${fontSizeM}; - --eui-font-size-s: ${fontSizeS}; - --eui-font-size-xs: ${fontSizeXS}; - --eui-font-size-xxs: ${fontSizeXXS}; - - --eui-line-height-base: ${lineHeightXL}; - --eui-line-height-xxl: ${lineHeightXXL}; - --eui-line-height-xl: ${lineHeightXL}; - --eui-line-height-l: ${lineHeightL}; - --eui-line-height-m: ${lineHeightM}; - --eui-line-height-s: ${lineHeightS}; - --eui-line-height-xs: ${lineHeightXS}; - --eui-line-height-xxs: ${lineHeightXXS}; - - --eui-size-xs: ${euiTheme.size.xs}; - --eui-size-s: ${euiTheme.size.s}; - - --eui-border-color-primary: ${euiBorderColor(theme, 'primary')}; - - --eui-theme-content-vertical-spacing: ${euiTheme.size.base}; - - /* Docusaurus theme variables */ - --ifm-font-family-base: ${fontBase.fontFamily}; - --ifm-font-size-base: var(--eui-font-size-base); - --ifm-font-weight-base: ${fontBase.fontWeight}; - --ifm-line-height-base: var(--eui-line-height-base); - - --ifm-h1-font-size: var(--eui-font-size-xl); - --ifm-h2-font-size: var(--eui-font-size-l); - --ifm-h3-font-size: var(--eui-font-size-m); - --ifm-h4-font-size: var(--eui-font-size-s); - --ifm-h5-font-size: var(--eui-font-size-xs); - --ifm-h6-font-size: var(--eui-font-size-xxs); - - --ifm-global-radius: ${euiTheme.border.radius.small}; - } + // color theme related variables + :root, + [data-theme='dark']:root { + /* EUI theme variables */ + --eui-background-color-primary: ${euiTheme.colors.backgroundBasePrimary}; + --eui-background-color-success: ${euiTheme.colors.backgroundBaseSuccess}; + --eui-background-color-danger: ${euiTheme.colors.backgroundBaseDanger}; + --eui-background-color-warning: ${euiTheme.colors.backgroundBaseWarning}; + --eui-background-color-accent: ${euiTheme.colors.backgroundBaseAccent}; + + --eui-color-danger-text: ${euiTheme.colors.textDanger}; + + /* Docusaurus theme variables */ + --ifm-background-color: ${colors.backgroundBasePlain}; + --ifm-font-color-base: ${colors.textParagraph}; + --ifm-link-color: ${colors.link}; + --ifm-link-hover-color: ${colors.link}; + + --ifm-menu-color: ${euiTheme.colors.textParagraph}; + --ifm-menu-color-background-active: ${euiTheme.colors + .backgroundBaseSubdued}; + --ifm-menu-color-background-hover: var(--eui-background-color-primary); + + --ifm-pre-background: ${euiTheme.colors.lightestShade}; + } + + :root { + /* EUI theme variables */ + --eui-font-size-base: ${fontBase.fontSize}; + --eui-font-size-xxl: ${fontSizeXXL}; + --eui-font-size-xl: ${fontSizeXL}; + --eui-font-size-l: ${fontSizeL}; + --eui-font-size-m: ${fontSizeM}; + --eui-font-size-s: ${fontSizeS}; + --eui-font-size-xs: ${fontSizeXS}; + --eui-font-size-xxs: ${fontSizeXXS}; + + --eui-line-height-base: ${lineHeightXL}; + --eui-line-height-xxl: ${lineHeightXXL}; + --eui-line-height-xl: ${lineHeightXL}; + --eui-line-height-l: ${lineHeightL}; + --eui-line-height-m: ${lineHeightM}; + --eui-line-height-s: ${lineHeightS}; + --eui-line-height-xs: ${lineHeightXS}; + --eui-line-height-xxs: ${lineHeightXXS}; + + --eui-size-xs: ${euiTheme.size.xs}; + --eui-size-s: ${euiTheme.size.s}; + + --eui-border-color-primary: ${euiTheme.colors.borderBasePrimary}; + + --eui-theme-content-vertical-spacing: ${euiTheme.size.base}; + + /* Docusaurus theme variables */ + --ifm-font-family-base: ${fontBase.fontFamily}; + --ifm-font-size-base: var(--eui-font-size-base); + --ifm-font-weight-base: ${fontBase.fontWeight}; + --ifm-line-height-base: var(--eui-line-height-base); + + --ifm-h1-font-size: var(--eui-font-size-xl); + --ifm-h2-font-size: var(--eui-font-size-l); + --ifm-h3-font-size: var(--eui-font-size-m); + --ifm-h4-font-size: var(--eui-font-size-s); + --ifm-h5-font-size: var(--eui-font-size-xs); + --ifm-h6-font-size: var(--eui-font-size-xxs); + + --ifm-global-radius: ${euiTheme.border.radius.small}; + } `; }; diff --git a/packages/eui-theme-borealis/.eslintcache b/packages/eui-theme-borealis/.eslintcache new file mode 100644 index 00000000000..5231400c151 --- /dev/null +++ b/packages/eui-theme-borealis/.eslintcache @@ -0,0 +1 @@ +[{"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/index.ts":"1","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_animation.ts":"2","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_borders.ts":"3","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_breakpoint.ts":"4","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_colors.ts":"5","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_colors_dark.ts":"6","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_colors_light.ts":"7","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_levels.ts":"8","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_size.ts":"9","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_states.ts":"10","/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_typography.ts":"11"},{"size":1110,"mtime":1728391334458,"results":"12","hashOfConfig":"13"},{"size":896,"mtime":1728391334459,"results":"14","hashOfConfig":"13"},{"size":1074,"mtime":1728391334459,"results":"15","hashOfConfig":"13"},{"size":531,"mtime":1728391334460,"results":"16","hashOfConfig":"13"},{"size":637,"mtime":1728391334460,"results":"17","hashOfConfig":"13"},{"size":2144,"mtime":1728391334461,"results":"18","hashOfConfig":"13"},{"size":2143,"mtime":1728391334461,"results":"19","hashOfConfig":"13"},{"size":615,"mtime":1728391334462,"results":"20","hashOfConfig":"13"},{"size":895,"mtime":1728391334462,"results":"21","hashOfConfig":"13"},{"size":744,"mtime":1728391334463,"results":"22","hashOfConfig":"13"},{"size":1576,"mtime":1728391334463,"results":"23","hashOfConfig":"13"},{"filePath":"24","messages":"25","suppressedMessages":"26","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1h5kx7l",{"filePath":"27","messages":"28","suppressedMessages":"29","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"30","messages":"31","suppressedMessages":"32","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"33","messages":"34","suppressedMessages":"35","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"36","messages":"37","suppressedMessages":"38","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"39","messages":"40","suppressedMessages":"41","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"42","messages":"43","suppressedMessages":"44","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"45","messages":"46","suppressedMessages":"47","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"48","messages":"49","suppressedMessages":"50","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"51","messages":"52","suppressedMessages":"53","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"54","messages":"55","suppressedMessages":"56","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/index.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_animation.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_borders.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_breakpoint.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_colors.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_colors_dark.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_colors_light.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_levels.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_size.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_states.ts",[],[],"/Users/lenegadewoll/Documents/Projects/eui/packages/eui-theme-borealis/src/variables/_typography.ts",[],[]] \ No newline at end of file diff --git a/packages/eui-theme-borealis/.eslintignore b/packages/eui-theme-borealis/.eslintignore new file mode 100644 index 00000000000..4874b17b0b8 --- /dev/null +++ b/packages/eui-theme-borealis/.eslintignore @@ -0,0 +1,9 @@ +dist +node_modules +lib +types +**/*.d.ts +package.json +scripts +.eslintrc.js +babel.config.js diff --git a/packages/eui-theme-borealis/.eslintplugin.js b/packages/eui-theme-borealis/.eslintplugin.js new file mode 100644 index 00000000000..88d38f7aa99 --- /dev/null +++ b/packages/eui-theme-borealis/.eslintplugin.js @@ -0,0 +1,3 @@ +exports.rules = { + 'require-license-header': require('@elastic/eui-theme-common/scripts/eslint-plugin-local/require_license_header.js'), +}; diff --git a/packages/eui-theme-borealis/.eslintrc.js b/packages/eui-theme-borealis/.eslintrc.js new file mode 100644 index 00000000000..e33853c0255 --- /dev/null +++ b/packages/eui-theme-borealis/.eslintrc.js @@ -0,0 +1,114 @@ +const SSPL_ELASTIC_2_0_LICENSE_HEADER = ` +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +`; + +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: ['./tsconfig.json'], + ecmaFeatures: { + jsx: true, + }, + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.ts', '.tsx', '.js', '.json'], + }, + }, + }, + extends: [ + 'plugin:@typescript-eslint/recommended', + // Prettier options need to come last, in order to override other style rules + 'plugin:prettier/recommended', + ], + plugins: ['local', 'import'], + rules: { + 'block-scoped-var': 'error', + camelcase: 'off', + 'dot-notation': ['error', { allowKeywords: true }], + eqeqeq: ['error', 'always', { null: 'ignore' }], + 'guard-for-in': 'error', + 'new-cap': ['error', { capIsNewExceptions: ['Private'] }], + 'no-caller': 'error', + 'no-const-assign': 'error', + 'no-debugger': 'error', + 'no-empty': ['error', { allowEmptyCatch: true }], + 'no-eval': 'error', + 'no-extend-native': 'error', + 'no-global-assign': 'error', + 'no-loop-func': 'error', + 'no-restricted-globals': ['error', 'context'], + 'no-script-url': 'error', + 'no-sequences': 'error', + 'no-var': 'error', + 'no-with': 'error', + 'prefer-const': 'error', + 'prefer-template': 'error', + strict: ['error', 'never'], + 'valid-typeof': 'error', + + 'local/require-license-header': [ + 'warn', + { + license: SSPL_ELASTIC_2_0_LICENSE_HEADER, + }, + ], + + 'import/no-unresolved': ['error', { amd: true, commonjs: true }], + 'import/namespace': 'error', + 'import/default': 'error', + 'import/export': 'error', + 'import/no-named-as-default': 'error', + 'import/no-named-as-default-member': 'error', + 'import/no-duplicates': 'error', + + '@typescript-eslint/array-type': ['error', { default: 'array-simple' }], + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-member-accessibility': 'off', + '@typescript-eslint/indent': 'off', + '@typescript-eslint/ban-tslint-comment': 'error', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-parameter-properties': 'off', + '@typescript-eslint/no-triple-slash-reference': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', ignoreRestSiblings: true }, + ], + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-empty-function': 'off', + // It"s all very well saying that some types are trivially inferrable, + // but being explicit is still clearer. + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/naming-convention': 'off', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-ignore': 'allow-with-description', + 'ts-expect-error': 'allow-with-description', + }, + ], + '@typescript-eslint/consistent-type-exports': [ + 'error', + { fixMixedExportsWithInlineTypeSpecifier: false }, + ], + }, + overrides: [ + { + files: ['*.d.ts'], + rules: { + 'react/prefer-es6-class': 'off', + }, + }, + ], +}; diff --git a/packages/eui-theme-borealis/.gitignore b/packages/eui-theme-borealis/.gitignore new file mode 100644 index 00000000000..1a3f60d4707 --- /dev/null +++ b/packages/eui-theme-borealis/.gitignore @@ -0,0 +1,8 @@ +# Dependencies +/node_modules + +# Production +/lib + +yarn-debug.log* +yarn-error.log* \ No newline at end of file diff --git a/packages/eui-theme-borealis/.prettierrc b/packages/eui-theme-borealis/.prettierrc new file mode 100644 index 00000000000..b2f0fa8f00e --- /dev/null +++ b/packages/eui-theme-borealis/.prettierrc @@ -0,0 +1,7 @@ +{ + "parser": "typescript", + "printWidth": 80, + "semi": true, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/packages/eui-theme-borealis/.stylelintrc.js b/packages/eui-theme-borealis/.stylelintrc.js new file mode 100644 index 00000000000..42adc41f28c --- /dev/null +++ b/packages/eui-theme-borealis/.stylelintrc.js @@ -0,0 +1,163 @@ +const camelCaseRegex = '^[a-z][\\w-]*$'; // Note: also allows `_` as part of BEM naming +const camelCaseValueRegex = '/^[a-z][\\w.]*$/'; // Note: also allows `.` for JS objects +const cssInJsVarRegex = '/\\${[a-zA-Z]+/'; + +module.exports = { + // @see https://stylelint.io/user-guide/rules + rules: { + // Enforce camelCase naming + 'selector-class-pattern': camelCaseRegex, + 'keyframes-name-pattern': camelCaseRegex, + 'custom-property-pattern': camelCaseRegex, + + // Opinionated rules + 'declaration-no-important': true, + 'max-nesting-depth': [ + 2, + { + ignore: ['blockless-at-rules', 'pseudo-classes'], + }, + ], + 'block-no-empty': true, + 'selector-no-qualifying-type': [ + true, + { + ignore: ['attribute'], // Allows input[type=search] + }, + ], + + // Non-Prettier newline rules + // Put a line-break between sections of CSS, but allow quick one-liners for legibility + 'rule-empty-line-before': [ + 'always-multi-line', + { + ignore: ['first-nested', 'after-comment'], + }, + ], + 'comment-empty-line-before': null, + 'declaration-empty-line-before': null, + + // Value preferences + 'number-max-precision': null, + // Attempt to catch/flag non-variable color values + 'color-named': 'never', + 'color-no-hex': true, + // Prefer lowercase values, except for font names and currentColor + 'value-keyword-case': [ + 'lower', + { + ignoreProperties: ['font-family', '/^\\$eui[\\w]+/'], // Allow fonts and Sass variables + ignoreKeywords: ['currentColor'], + }, + ], + 'declaration-block-no-duplicate-properties': [ + true, + { + ignore: ['consecutive-duplicates'], // We occasionally use duplicate property values for cross-browser fallbacks + }, + ], + + // TODO: It may be worth investigating and updating these rules to their more modern counterparts + 'color-function-notation': 'legacy', + 'alpha-value-notation': 'number', + + // Disable various opinionated extended stylelint rules that EUI has not previously enforced + 'no-descending-specificity': null, + 'keyframe-selector-notation': null, + 'declaration-block-no-redundant-longhand-properties': null, + }, + overrides: [ + { + // TODO: Remove Sass-specific config & rules once we're completely off Sass + files: ['**/*.scss'], + ignoreFiles: [ + 'generator-eui/**/*.scss', + 'src/global_styling/react_date_picker/**/*.scss', + 'src/themes/amsterdam/global_styling/react_date_picker/**/*.scss', + 'src/components/date_picker/react-datepicker/**/*.scss', + ], + extends: [ + 'stylelint-config-standard-scss', + 'stylelint-config-prettier-scss', + ], + // @see https://github.com/stylelint-scss/stylelint-scss#list-of-rules + rules: { + // Casing + 'scss/dollar-variable-pattern': camelCaseRegex, + 'scss/at-mixin-pattern': camelCaseRegex, + 'scss/at-function-pattern': camelCaseRegex, + 'function-name-case': [ + 'lower', + { + ignoreFunctions: [`/${camelCaseRegex}/`, 'MIN'], + }, + ], + + // Whitespace/newlines + 'scss/at-if-closing-brace-space-after': 'always-intermediate', + 'scss/operator-no-unspaced': true, + + // Formatting rules deprecated as of v15 - keep them in Sass styles just in case until end of migration + // @see https://github.com/stylelint/stylelint/blob/main/docs/user-guide/rules.md#deprecated + 'color-hex-case': 'upper', + 'string-quotes': 'single', + // 2 spaces for indentation + indentation: [2, { indentInsideParens: 'once-at-root-twice-in-block' }], + // Mimic 1tbs `} else {` brace style, like our JS + 'block-opening-brace-space-before': 'always', + 'block-closing-brace-newline-before': 'always-multi-line', + // Ensure multiple selectors on one line each + 'selector-list-comma-newline-before': 'never-multi-line', + 'selector-list-comma-newline-after': 'always', + // Trim unnecessary newlines/whitespace + 'block-closing-brace-empty-line-before': 'never', + 'max-empty-lines': 1, + 'no-eol-whitespace': true, + // Enforce spacing around various syntax symbols (colons, operators, etc.) + 'declaration-colon-space-after': 'always-single-line', + 'declaration-colon-space-before': 'never', + 'function-calc-no-unspaced-operator': true, + 'selector-combinator-space-before': 'always', + 'selector-combinator-space-after': 'always', + // Ensure trailing semicolons are always present on non-oneliners + 'declaration-block-semicolon-newline-after': 'always-multi-line', + + // Disable various opinionated extended stylelint rules that EUI has not previously enforced + 'scss/no-global-function-names': null, + 'scss/dollar-variable-empty-line-before': null, + 'scss/at-rule-conditional-no-parentheses': null, + 'scss/double-slash-comment-empty-line-before': null, + 'scss/at-if-no-null': null, + 'selector-not-notation': null, // Enforce comma notation for CSS-in-JS moving forward + }, + }, + { + files: ['**/*.styles.ts', '**/*.ts', '**/*.tsx'], + extends: ['stylelint-config-standard'], + customSyntax: 'postcss-styled-syntax', + rules: { + // Unfortunately, double slash comments must be replaced with standard CSS /* */ comments + // Otherwise we get a parsing error - see https://github.com/hudochenkov/postcss-styled-syntax#known-issues + 'no-invalid-double-slash-comments': true, + // Empty style keys should be allowed, as Emotion still uses them for generating classNames + 'no-empty-source': null, + // Don't lint casing on interpolated JS vars + 'function-name-case': ['lower', { ignoreFunctions: [cssInJsVarRegex] }], + 'function-no-unknown': [true, { ignoreFunctions: [cssInJsVarRegex] }], + 'value-keyword-case': [ + 'lower', + { + ignoreProperties: ['font-family'], + ignoreKeywords: [camelCaseValueRegex], + }, + ], + // This is set to deprecate after stylelint v16, but in the meanwhile, is helpful + // for finding extraneous semicolons after utils that already output semicolons (e.g. logicalCSS()) + 'no-extra-semicolons': true, + + // Emotion uses the `label` property to generate the output className string + 'property-no-unknown': [true, { ignoreProperties: 'label' }], + }, + }, + ], +}; diff --git a/packages/eui-theme-borealis/LICENSE.txt b/packages/eui-theme-borealis/LICENSE.txt new file mode 100644 index 00000000000..74327a8f6f3 --- /dev/null +++ b/packages/eui-theme-borealis/LICENSE.txt @@ -0,0 +1,6 @@ +Source code in this repository is covered by (i) a dual license under the Server +Side Public License, v 1 and the Elastic License 2.0 or (ii) an Apache License +2.0 compatible license or (iii) solely under the Elastic License 2.0, in each +case, as noted in the applicable header. The default throughout the repository +is a dual license under the Server Side Public License, v 1 and the Elastic +License 2.0, unless the header specifies another license. diff --git a/packages/eui-theme-borealis/README.md b/packages/eui-theme-borealis/README.md new file mode 100644 index 00000000000..cb7c237d4a2 --- /dev/null +++ b/packages/eui-theme-borealis/README.md @@ -0,0 +1,19 @@ +# EUI theme **Borealis** + +This package contains specific style tokens for the EUI theme **Borealis** which is exported as `EuiThemeBorealis`. + +## Installing dependencies + +Please run `yarn` to install dependencies: + +```shell +yarn +``` + +## Building helper packages + +Before you run scripts, it's mandatory to build local dependency packages: + +```shell +yarn workspaces foreach -Rti --from @elastic/eui-theme-borealis run build +``` \ No newline at end of file diff --git a/packages/eui-theme-borealis/package.json b/packages/eui-theme-borealis/package.json new file mode 100644 index 00000000000..d56f9924db2 --- /dev/null +++ b/packages/eui-theme-borealis/package.json @@ -0,0 +1,60 @@ +{ + "name": "@elastic/eui-theme-borealis", + "version": "0.0.1", + "description": "A visual theme for EUI", + "license": "SEE LICENSE IN LICENSE.txt", + "scripts": { + "build:workspaces": "yarn workspaces foreach -Rti --from @elastic/eui-theme-borealis --exclude @elastic/eui-theme-borealis run build", + "build": "tsc", + "build-pack": "yarn build && npm pack", + "lint": "yarn tsc --noEmit && yarn lint-es && yarn lint-sass", + "lint-es": "eslint --cache src/**/*.ts --max-warnings 0", + "lint-sass": "yarn stylelint \"**/*.scss\" --quiet-deprecation-warnings", + "test": "jest", + "pre-push": "yarn build:workspaces && yarn lint && yarn test" + }, + "repository": { + "type": "git", + "url": "https://github.com/elastic/eui.git", + "directory": "packages/eui-theme-borealis" + }, + "private": true, + "devDependencies": { + "@elastic/eui-theme-common": "workspace:^", + "@types/jest": "^29.5.12", + "@types/prettier": "2.7.3", + "@typescript-eslint/eslint-plugin": "^5.59.7", + "@typescript-eslint/parser": "^5.59.7", + "eslint": "^8.41.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jest": "^28.5.0", + "eslint-plugin-local": "^1.0.0", + "eslint-plugin-prettier": "^4.2.1", + "jest": "^29.7.0", + "prettier": "^2.8.8", + "stylelint": "^15.7.0", + "stylelint-config-prettier-scss": "^1.0.0", + "stylelint-config-standard": "^33.0.0", + "stylelint-config-standard-scss": "^9.0.0", + "typescript": "^5.6.2" + }, + "peerDependencies": { + "@elastic/eui-theme-common": "0.0.1" + }, + "main": "lib/index.js", + "exports": { + "./lib/*": "./lib/*", + ".": { + "default": "./lib/index.js" + } + }, + "files": [ + "lib/", + "src/**/*.scss", + "README.md" + ], + "installConfig": { + "hoistingLimits": "workspaces" + } +} diff --git a/packages/eui-theme-borealis/src/index.ts b/packages/eui-theme-borealis/src/index.ts new file mode 100644 index 00000000000..2f1f2247be7 --- /dev/null +++ b/packages/eui-theme-borealis/src/index.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { buildTheme, EuiThemeShape } from '@elastic/eui-theme-common'; + +import { colors } from './variables/colors'; +import { animation } from './variables/_animation'; +import { breakpoint } from './variables/_breakpoint'; +import { base, size } from './variables/_size'; +import { border } from './variables/_borders'; +import { levels } from './variables/_levels'; +import { font } from './variables/_typography'; +import { focus } from './variables/_states'; +import { components } from './variables/_components'; + +export const EUI_THEME_BOREALIS_KEY = 'EUI_THEME_BOREALIS'; + +export const euiThemeBorealis: EuiThemeShape = { + colors, + base, + size, + border, + font, + animation, + breakpoint, + levels, + focus, + components, +}; + +export const EuiThemeBorealis = buildTheme( + euiThemeBorealis, + EUI_THEME_BOREALIS_KEY +); diff --git a/packages/eui-theme-borealis/src/theme_dark.scss b/packages/eui-theme-borealis/src/theme_dark.scss new file mode 100644 index 00000000000..63e6fd59d20 --- /dev/null +++ b/packages/eui-theme-borealis/src/theme_dark.scss @@ -0,0 +1,6 @@ +// color mode specific variables +@import './variables/colors/colors_dark'; + +// Global styling +@import 'node_modules/@elastic/eui-theme-common/src/global_styling/index'; +@import './variables/index'; \ No newline at end of file diff --git a/packages/eui-theme-borealis/src/theme_light.scss b/packages/eui-theme-borealis/src/theme_light.scss new file mode 100644 index 00000000000..2029987c367 --- /dev/null +++ b/packages/eui-theme-borealis/src/theme_light.scss @@ -0,0 +1,6 @@ +// color mode specific variables +@import './variables/colors/colors_light'; + +// Global styling +@import 'node_modules/@elastic/eui-theme-common/src/global_styling/index'; +@import './variables/index'; \ No newline at end of file diff --git a/packages/eui-theme-borealis/src/variables/_animation.ts b/packages/eui-theme-borealis/src/variables/_animation.ts new file mode 100644 index 00000000000..84933441db3 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_animation.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { + _EuiThemeAnimationSpeeds, + _EuiThemeAnimationEasings, + _EuiThemeAnimation, +} from '@elastic/eui-theme-common'; + +export const animation_speed: _EuiThemeAnimationSpeeds = { + extraFast: '90ms', + fast: '150ms', + normal: '250ms', + slow: '350ms', + extraSlow: '500ms', +}; + +export const animation_ease: _EuiThemeAnimationEasings = { + bounce: 'cubic-bezier(.34, 1.61, .7, 1)', + resistance: 'cubic-bezier(.694, .0482, .335, 1)', +}; + +export const animation: _EuiThemeAnimation = { + ...animation_speed, + ...animation_ease, +}; diff --git a/packages/eui-theme-borealis/src/variables/_borders.scss b/packages/eui-theme-borealis/src/variables/_borders.scss new file mode 100644 index 00000000000..4ab0d200c91 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_borders.scss @@ -0,0 +1,3 @@ +// Borders + +$euiBorderColor: $euiColorBorderPlain !default; diff --git a/packages/eui-theme-borealis/src/variables/_borders.ts b/packages/eui-theme-borealis/src/variables/_borders.ts new file mode 100644 index 00000000000..34012006168 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_borders.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + computed, + type _EuiThemeBorder, + sizeToPixel, +} from '@elastic/eui-theme-common'; + +export const border: _EuiThemeBorder = { + color: computed( + ([borderBasePlain]) => borderBasePlain, + ['colors.borderBasePlain'] + ), + width: { + thin: '1px', + thick: '2px', + }, + radius: { + medium: computed(sizeToPixel(0.375)), + small: computed(sizeToPixel(0.25)), + }, + thin: computed( + ([width, color]) => `${width.thin} solid ${color}`, + ['border.width', 'border.color'] + ), + thick: computed( + ([width, color]) => `${width.thick} solid ${color}`, + ['border.width', 'border.color'] + ), + editable: computed( + ([width, color]) => `${width.thick} dotted ${color}`, + ['border.width', 'border.color'] + ), +}; diff --git a/packages/eui-theme-borealis/src/variables/_breakpoint.ts b/packages/eui-theme-borealis/src/variables/_breakpoint.ts new file mode 100644 index 00000000000..facd8e14ba4 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_breakpoint.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { _EuiThemeBreakpoints } from '@elastic/eui-theme-common'; + +export const breakpoint: _EuiThemeBreakpoints = { + xl: 1200, + l: 992, + m: 768, + s: 575, + xs: 0, +}; diff --git a/packages/eui-theme-borealis/src/variables/_buttons.scss b/packages/eui-theme-borealis/src/variables/_buttons.scss new file mode 100644 index 00000000000..624e96cb0b8 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_buttons.scss @@ -0,0 +1,4 @@ +$euiButtonColorDisabled: $euiColorDisabled; +$euiButtonColorDisabledText: $euiColorTextDisabled; +$euiButtonDefaultTransparency: 0.8; +$euiButtonFontWeight: $euiFontWeightMedium; diff --git a/packages/eui-theme-borealis/src/variables/_buttons.ts b/packages/eui-theme-borealis/src/variables/_buttons.ts new file mode 100644 index 00000000000..bc7a5f8fe2f --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_buttons.ts @@ -0,0 +1,104 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { _EuiThemeButton } from '@elastic/eui-theme-common'; +import { SEMANTIC_COLORS } from './colors/_semantic_colors'; +import { + background_colors, + brand_text_colors, + text_colors, +} from './colors/_colors_light'; +import { + dark_background_colors, + dark_brand_text_colors, + dark_text_colors, +} from './colors/_colors_dark'; + +const _buttons = { + backgroundPrimary: background_colors.backgroundLightPrimary, + backgroundAccent: background_colors.backgroundLightAccent, + backgroundAccentSecondary: background_colors.backgroundLightAccentSecondary, + backgroundSuccess: background_colors.backgroundLightSuccess, + backgroundWarning: background_colors.backgroundLightWarning, + backgroundDanger: background_colors.backgroundLightDanger, + backgroundText: background_colors.backgroundLightText, + backgroundDisabled: background_colors.backgroundBaseDisabled, + + backgroundFilledPrimary: background_colors.backgroundFilledPrimary, + backgroundFilledAccent: background_colors.backgroundFilledAccent, + backgroundFilledAccentSecondary: + background_colors.backgroundFilledAccentSecondary, + backgroundFilledSuccess: background_colors.backgroundFilledSuccess, + backgroundFilledWarning: background_colors.backgroundFilledWarning, + backgroundFilledDanger: background_colors.backgroundFilledDanger, + backgroundFilledText: background_colors.backgroundFilledText, + backgroundFilledDisabled: background_colors.backgroundBaseDisabled, + + textColorPrimary: brand_text_colors.textPrimary, + textColorAccent: brand_text_colors.textAccent, + textColorAccentSecondary: brand_text_colors.textAccentSecondary, + textColorSuccess: brand_text_colors.textSuccess, + textColorWarning: brand_text_colors.textWarning, + textColorDanger: brand_text_colors.textDanger, + textColorText: text_colors.textParagraph, + textColorDisabled: text_colors.textDisabled, + + textColorFilledPrimary: text_colors.textInverse, + textColorFilledAccent: text_colors.textInverse, + textColorFilledAccentSecondary: text_colors.textInverse, + textColorFilledSuccess: text_colors.textInverse, + textColorFilledWarning: SEMANTIC_COLORS.warning110, + textColorFilledDanger: text_colors.textInverse, + textColorFilledText: text_colors.textInverse, + textColorFilledDisabled: text_colors.textDisabled, +}; + +const _dark_buttons = { + backgroundPrimary: dark_background_colors.backgroundLightPrimary, + backgroundAccent: dark_background_colors.backgroundLightAccent, + backgroundAccentSecondary: + dark_background_colors.backgroundLightAccentSecondary, + backgroundSuccess: dark_background_colors.backgroundLightSuccess, + backgroundWarning: dark_background_colors.backgroundLightWarning, + backgroundDanger: dark_background_colors.backgroundLightDanger, + backgroundText: dark_background_colors.backgroundLightText, + backgroundDisabled: dark_background_colors.backgroundBaseDisabled, + + backgroundFilledPrimary: dark_background_colors.backgroundFilledPrimary, + backgroundFilledAccent: dark_background_colors.backgroundFilledAccent, + backgroundFilledAccentSecondary: + dark_background_colors.backgroundFilledAccentSecondary, + backgroundFilledSuccess: dark_background_colors.backgroundFilledSuccess, + backgroundFilledWarning: dark_background_colors.backgroundFilledWarning, + backgroundFilledDanger: dark_background_colors.backgroundFilledDanger, + backgroundFilledText: dark_background_colors.backgroundFilledText, + backgroundFilledDisabled: dark_background_colors.backgroundBaseDisabled, + + textColorPrimary: dark_brand_text_colors.textPrimary, + textColorAccent: dark_brand_text_colors.textAccent, + textColorAccentSecondary: dark_brand_text_colors.textAccentSecondary, + textColorSuccess: dark_brand_text_colors.textSuccess, + textColorWarning: dark_brand_text_colors.textWarning, + textColorDanger: dark_brand_text_colors.textDanger, + textColorText: dark_text_colors.textParagraph, + textColorDisabled: dark_text_colors.textDisabled, + + textColorFilledPrimary: dark_text_colors.textInverse, + textColorFilledAccent: dark_text_colors.textInverse, + textColorFilledAccentSecondary: dark_text_colors.textInverse, + textColorFilledSuccess: dark_text_colors.textInverse, + textColorFilledWarning: SEMANTIC_COLORS.warning110, + textColorFilledDanger: dark_text_colors.textInverse, + textColorFilledText: dark_text_colors.textInverse, + textColorFilledDisabled: dark_text_colors.textDisabled, +}; + +export const buttons: _EuiThemeButton = { + LIGHT: _buttons, + DARK: _dark_buttons, +}; diff --git a/packages/eui-theme-borealis/src/variables/_components.ts b/packages/eui-theme-borealis/src/variables/_components.ts new file mode 100644 index 00000000000..63c25c074f8 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_components.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { _EuiThemeComponents } from '@elastic/eui-theme-common'; + +import { buttons } from './_buttons'; + +export const components: _EuiThemeComponents = { + buttons, +}; diff --git a/packages/eui-theme-borealis/src/variables/_index.scss b/packages/eui-theme-borealis/src/variables/_index.scss new file mode 100644 index 00000000000..3b4275acca0 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_index.scss @@ -0,0 +1,8 @@ +// Import base theme first, then override +@import 'node_modules/@elastic/eui-theme-common/src/global_styling/variables/index'; +@import 'states'; + +@import 'borders'; +@import 'buttons'; +@import 'page'; +@import 'typography'; diff --git a/packages/eui-theme-borealis/src/variables/_levels.ts b/packages/eui-theme-borealis/src/variables/_levels.ts new file mode 100644 index 00000000000..1f4e2db0bb7 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_levels.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { _EuiThemeLevels } from '@elastic/eui-theme-common'; + +export const levels: _EuiThemeLevels = { + toast: 9000, + modal: 8000, + mask: 6000, + navigation: 6000, + menu: 2000, + header: 1000, + flyout: 1000, + maskBelowHeader: 1000, + content: 0, +}; diff --git a/packages/eui-theme-borealis/src/variables/_page.scss b/packages/eui-theme-borealis/src/variables/_page.scss new file mode 100644 index 00000000000..318cae55681 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_page.scss @@ -0,0 +1 @@ +$euiPageDefaultMaxWidth: map-get($euiBreakpoints, 'xl'); diff --git a/packages/eui-theme-borealis/src/variables/_size.ts b/packages/eui-theme-borealis/src/variables/_size.ts new file mode 100644 index 00000000000..1b3fe6a8ea2 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_size.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + computed, + sizeToPixel, + type _EuiThemeBase, + type _EuiThemeSizes, +} from '@elastic/eui-theme-common'; + +export const base: _EuiThemeBase = 16; + +export const size: _EuiThemeSizes = { + xxs: computed(sizeToPixel(0.125)), + xs: computed(sizeToPixel(0.25)), + s: computed(sizeToPixel(0.5)), + m: computed(sizeToPixel(0.75)), + base: computed(sizeToPixel()), + l: computed(sizeToPixel(1.5)), + xl: computed(sizeToPixel(2)), + xxl: computed(sizeToPixel(2.5)), + xxxl: computed(sizeToPixel(3)), + xxxxl: computed(sizeToPixel(4)), +}; diff --git a/packages/eui-theme-borealis/src/variables/_states.scss b/packages/eui-theme-borealis/src/variables/_states.scss new file mode 100644 index 00000000000..13e30784936 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_states.scss @@ -0,0 +1,11 @@ +// Color when not using currentColor +$euiFocusRingColor: $euiColorPrimaryText; + +// Sizing +$euiFocusRingAnimStartSize: 2px; +$euiFocusRingSize: 2px; + +// Transparency +$euiFocusTransparency: lightOrDarkTheme(.9, .8); +$euiFocusTransparencyPercent: lightOrDarkTheme(90%, 80%); +$euiFocusBackgroundColor: transparentize($euiColorPrimary, $euiFocusTransparency); diff --git a/packages/eui-theme-borealis/src/variables/_states.ts b/packages/eui-theme-borealis/src/variables/_states.ts new file mode 100644 index 00000000000..776a00ffeca --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_states.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + computed, + sizeToPixel, + type _EuiThemeFocus, +} from '@elastic/eui-theme-common'; + +export const focus: _EuiThemeFocus = { + // Focus ring + color: 'currentColor', + width: computed(sizeToPixel(0.125)), + // Focus background + transparency: { LIGHT: 0.1, DARK: 0.2 }, + backgroundColor: 'rgba(0, 119, 204, 0.1)', // temp. static value to remove dependency on transparentize +}; diff --git a/packages/eui-theme-borealis/src/variables/_typography.scss b/packages/eui-theme-borealis/src/variables/_typography.scss new file mode 100644 index 00000000000..824f56b42da --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_typography.scss @@ -0,0 +1,64 @@ +// Finally start using the non-beta version of Inter +$euiFontFamily: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol' !default; + +// Font sizes -- scale is loosely based on Major Third (1.250) with a base of 14px +// Base list is an altered scale based on 16px to match the resulted values below +$euiTextScale: 2.125, 1.6875, 1.375, 1.125, 1, .875, .75 !default; + +$euiFontSize: $euiSize - 2; // 14px + +$euiFontSizeXS: floor($euiFontSize * .86); // 12px // h6 +$euiFontSizeS: floor($euiFontSize * 1); // 14px // h5 --> Now the same as the base $euiFontSize +$euiFontSizeM: ceil($euiFontSize * 1.14); // 16px // h4 +$euiFontSizeL: ceil($euiFontSize * 1.57); // 22px // h3 +$euiFontSizeXL: floor($euiFontSize * 1.93); // 27px // h2 +$euiFontSizeXXL: floor($euiFontSize * 2.43); // 34px // h1 + +$euiBodyLineHeight: 1.142857143 !default; // 16px from a 14px base font size to ensure it aligns to our 16px grid + +$euiCodeFontWeightRegular: 400; +$euiCodeFontWeightBold: 700; + +// Normally functions are imported before variables in `_index.scss` files +// But because they need to consume some typography variables they need to live here +@function convertToRem($size) { + @return #{$size / $euiFontSize}rem; +} + +// Use 8px increments for base gridline +@function lineHeightFromBaseline($multiplier: 3) { + @return convertToRem(($euiSize / 2) * $multiplier); +} + +$euiTitles: ( + 'xxxs': ( + 'font-size': $euiFontSizeXS, + 'line-height': lineHeightFromBaseline(2), + 'font-weight': $euiFontWeightBold, + ), + 'xxs': ( + 'font-size': $euiFontSizeS, + 'line-height': lineHeightFromBaseline(3), + 'font-weight': $euiFontWeightBold, + ), + 'xs': ( + 'font-size': $euiFontSizeM, + 'line-height': lineHeightFromBaseline(3), + 'font-weight': $euiFontWeightBold, + ), + 's': ( + 'font-size': $euiFontSizeL, + 'line-height': lineHeightFromBaseline(4), + 'font-weight': $euiFontWeightBold, + ), + 'm': ( + 'font-size': $euiFontSizeXL, + 'line-height': lineHeightFromBaseline(4), + 'font-weight': $euiFontWeightBold, + ), + 'l': ( + 'font-size': $euiFontSizeXXL, + 'line-height': lineHeightFromBaseline(5), + 'font-weight': $euiFontWeightBold, + ), +); diff --git a/packages/eui-theme-borealis/src/variables/_typography.ts b/packages/eui-theme-borealis/src/variables/_typography.ts new file mode 100644 index 00000000000..6454c7b239c --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/_typography.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + computed, + type _EuiThemeFont, + type _EuiThemeFontBase, + type _EuiThemeFontScales, + type _EuiThemeFontWeights, +} from '@elastic/eui-theme-common'; + +// Typographic scale -- loosely based on Major Third (1.250) +export const fontScale: _EuiThemeFontScales = { + xxxs: 0.5625, + xxs: 0.6875, + xs: 0.75, + s: 0.875, + m: 1, + l: 1.375, + xl: 1.6875, + xxl: 2.125, +}; + +// Families & base font settings +export const fontBase: _EuiThemeFontBase = { + family: "'Inter', BlinkMacSystemFont, Helvetica, Arial, sans-serif", + familyCode: "'Roboto Mono', Menlo, Courier, monospace", + familySerif: 'Georgia, Times, Times New Roman, serif', + + // Careful using ligatures. Code editors like ACE will often error because of width calculations + featureSettings: "'calt' 1, 'kern' 1, 'liga' 1", + defaultUnits: 'rem', + + baseline: computed(([base]) => base / 4, ['base']), + lineHeightMultiplier: 1.5, +}; + +export const fontWeight: _EuiThemeFontWeights = { + light: 300, + regular: 400, + medium: 500, + semiBold: 600, + bold: 700, +}; + +export const font: _EuiThemeFont = { + ...fontBase, + scale: fontScale, + weight: fontWeight, + body: { + scale: 's', + weight: 'regular', + }, + title: { + weight: 'bold', + }, +}; diff --git a/packages/eui-theme-borealis/src/variables/colors/_colors_dark.scss b/packages/eui-theme-borealis/src/variables/colors/_colors_dark.scss new file mode 100644 index 00000000000..a2dd533ad9e --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_colors_dark.scss @@ -0,0 +1,113 @@ +// stylelint-disable color-no-hex + +// This extra import allows any variables that are created via functions to work when loaded into JS +@import 'node_modules/@elastic/eui-theme-common/src/global_styling/functions/index'; + +@import 'semantic_colors'; + +// Constants (legacy) +$euiColorGhost: $euiColorWhite !default; +$euiColorInk: $euiColorBlack !default; + +// Constants +$euiColorPlainLight: $euiColorWhite; +$euiColorPlainDark: $euiColorBlueBlack; + +// Brand +$euiColorPrimary: $euiColorPrimary70 !default; +$euiColorAccent: $euiColorAccent70 !default; +$euiColorAccentSecondary: $euiColorAccentSecondary70 !default; +$euiColorSuccess: $euiColorSuccess60 !default; +$euiColorWarning: $euiColorWarning40 !default; +$euiColorDanger: $euiColorDanger70 !default; + +// Shades +$euiColorEmptyShade: $euiColorPlainDark !default; +$euiColorLightestShade: $euiColorShade135 !default; +$euiColorLightShade: $euiColorShade125 !default; +$euiColorMediumShade: $euiColorShade95 !default; +$euiColorDarkShade: $euiColorShade75 !default; +$euiColorDarkestShade: $euiColorShade30 !default; +$euiColorFullShade: $euiColorPlainLight !default; + +// Backgrounds +$euiPageBackgroundColor: $euiColorMutedBlack !default; // deprecated +$euiColorHighlight: $euiColorPrimary100 !default; + +$euiColorBackgroundBasePrimary: $euiColorPrimary130 !default; +$euiColorBackgroundBaseAccent: $euiColorAccent130 !default; +$euiColorBackgroundBaseAccentSecondary: $euiColorAccentSecondary130 !default; +$euiColorBackgroundBaseSuccess: $euiColorSuccess130 !default; +$euiColorBackgroundBaseWarning: $euiColorWarning130 !default; +$euiColorBackgroundBaseDanger: $euiColorDanger130 !default; +$euiColorBackgroundBasePlain: $euiColorShade140 !default; +$euiColorBackgroundBaseSubdued: $euiColorShade130 !default; +$euiColorBackgroundBaseDisabled: $euiColorShade125 !default; +$euiColorBackgroundBasePage: $euiColorPlainDark !default; +$euiColorBackgroundBasePrepend: $euiColorShade125 !default; + +$euiColorBackgroundBaseHover: $euiColorShade130 !default; +$euiColorBackgroundBaseSelect: $euiColorPrimary130 !default; +$euiColorBackgroundBaseOverlay: $euiColorTransparentBlack60 !default; + +$euiColorBackgroundLightPrimary: $euiColorPrimary120 !default; +$euiColorBackgroundLightAccent: $euiColorAccent120 !default; +$euiColorBackgroundLightAccentSecondary: $euiColorAccentSecondary120 !default; +$euiColorBackgroundLightSuccess: $euiColorSuccess120 !default; +$euiColorBackgroundLightWarning: $euiColorWarning120 !default; +$euiColorBackgroundLightDanger: $euiColorDanger120 !default; +$euiColorBackgroundLightText: $euiColorShade120 !default; + +$euiColorBackgroundFilledPrimary: $euiColorPrimary70 !default; +$euiColorBackgroundFilledAccent: $euiColorAccent70 !default; +$euiColorBackgroundFilledAccentSecondary: $euiColorAccentSecondary70 !default; +$euiColorBackgroundFilledSuccess: $euiColorSuccess70 !default; +$euiColorBackgroundFilledWarning: $euiColorWarning40 !default; +$euiColorBackgroundFilledDanger: $euiColorDanger70 !default; +$euiColorBackgroundFilledText: $euiColorShade70 !default; + +// Texts (legacy) +$euiTextColor: $euiColorShade20 !default; +$euiTitleColor: $euiColorShade15 !default; +$euiTextSubduedColor: $euiColorShade40 !default; +$euiColorDisabled: $euiColorBackgroundBaseDisabled !default; +$euiColorDisabledText: $euiColorShade60 !default; +$euiLinkColor: $euiColorPrimary60 !default; + +// Texts +$euiColorTextParagraph: $euiColorShade20 !default; +$euiColorTextHeading: $euiColorShade15 !default; +$euiColorTextSubdued: $euiColorShade40 !default; +$euiColorTextDisabled: $euiColorShade60 !default; +$euiColorTextInverse: $euiColorPlainDark !default; + +// Brand texts (legacy) +$euiColorPrimaryText: $euiColorPrimary60 !default; +$euiColorAccentText: $euiColorAccent60 !default; +$euiColorSuccessText: $euiColorSuccess60 !default; +$euiColorWarningText: $euiColorWarning40 !default; +$euiColorDangerText: $euiColorDanger60 !default; + +// Brand texts +$euiColorTextPrimary: $euiColorPrimary60 !default; +$euiColorTextAccent: $euiColorAccent60 !default; +$euiColorTextAccentSecondary: $euiColorAccentSecondary60 !default; +$euiColorTextSuccess: $euiColorSuccess60 !default; +$euiColorTextWarning: $euiColorWarning40 !default; +$euiColorTextDanger: $euiColorDanger60 !default; + +// Borders +$borderBasePrimary: $euiColorPrimary60; +$borderBaseAccent: $euiColorAccent60; +$borderBaseAccentSecondary: $euiColorAccentSecondary60; +$borderBaseSuccess: $euiColorSuccess60; +$borderBaseWarning: $euiColorWarning40; +$borderBaseDanger: $euiColorDanger60; +$borderBasePlain: $euiColorShade110; +$borderBaseSubdued: $euiColorShade120; +$borderBaseDisabled: $euiColorShade110; +$borderBaseFloating: $euiColorShade120; + +// Charts +$euiColorChartLines: $euiColorShade85 !default; +$euiColorChartBand: $euiColorShade125 !default; diff --git a/packages/eui-theme-borealis/src/variables/colors/_colors_dark.ts b/packages/eui-theme-borealis/src/variables/colors/_colors_dark.ts new file mode 100644 index 00000000000..0eed6c28bb2 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_colors_dark.ts @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + type _EuiThemeBrandColors, + type _EuiThemeBrandTextColors, + type _EuiThemeShadeColors, + type _EuiThemeSpecialColors, + type _EuiThemeTextColors, + type _EuiThemeColorsMode, + _EuiThemeBackgroundColors, + _EuiThemeBorderColors, + _EuiThemeTransparentBackgroundColors, +} from '@elastic/eui-theme-common'; + +import { PRIMITIVE_COLORS } from './_primitive_colors'; +import { SEMANTIC_COLORS } from './_semantic_colors'; + +/* + * DARK THEME + */ + +export const dark_brand_colors: _EuiThemeBrandColors = { + primary: SEMANTIC_COLORS.primary70, + accent: SEMANTIC_COLORS.accent70, + accentSecondary: SEMANTIC_COLORS.accentSecondary70, + success: SEMANTIC_COLORS.success60, + warning: SEMANTIC_COLORS.warning40, + danger: SEMANTIC_COLORS.danger70, +}; + +export const dark_brand_text_colors: _EuiThemeBrandTextColors = { + /* Legacy colors */ + primaryText: SEMANTIC_COLORS.primary60, + accentText: SEMANTIC_COLORS.accent60, + successText: SEMANTIC_COLORS.success60, + warningText: SEMANTIC_COLORS.warning60, + dangerText: SEMANTIC_COLORS.danger60, + + /* New colors */ + textPrimary: SEMANTIC_COLORS.primary60, + textAccent: SEMANTIC_COLORS.accent60, + textAccentSecondary: SEMANTIC_COLORS.accentSecondary60, + textSuccess: SEMANTIC_COLORS.success60, + textWarning: SEMANTIC_COLORS.warning60, + textDanger: SEMANTIC_COLORS.danger60, +}; + +export const dark_text_colors: _EuiThemeTextColors = { + /* Legacy colors */ + text: SEMANTIC_COLORS.shade20, + title: SEMANTIC_COLORS.shade15, + subduedText: SEMANTIC_COLORS.shade40, + link: SEMANTIC_COLORS.primary60, + + /* New colors */ + textParagraph: SEMANTIC_COLORS.shade20, + textHeading: SEMANTIC_COLORS.shade15, + textSubdued: SEMANTIC_COLORS.shade40, + textDisabled: SEMANTIC_COLORS.shade60, + textInverse: SEMANTIC_COLORS.plainDark, +}; + +/* TODO: These are not finalized yet. +These tokens won't be used in the new theme specifically, +but we want to support them until fully deprecated */ +export const dark_shades: _EuiThemeShadeColors = { + emptyShade: SEMANTIC_COLORS.plainDark, + lightestShade: SEMANTIC_COLORS.shade135, + lightShade: SEMANTIC_COLORS.shade125, + mediumShade: SEMANTIC_COLORS.shade95, + darkShade: SEMANTIC_COLORS.shade75, + darkestShade: SEMANTIC_COLORS.shade30, + fullShade: SEMANTIC_COLORS.plainLight, +}; + +export const dark_background_colors: _EuiThemeBackgroundColors = { + backgroundBasePrimary: SEMANTIC_COLORS.primary130, + backgroundBaseAccent: SEMANTIC_COLORS.accent130, + backgroundBaseAccentSecondary: SEMANTIC_COLORS.accentSecondary130, + backgroundBaseSuccess: SEMANTIC_COLORS.success130, + backgroundBaseWarning: SEMANTIC_COLORS.warning130, + backgroundBaseDanger: SEMANTIC_COLORS.danger130, + backgroundBasePlain: SEMANTIC_COLORS.shade140, + backgroundBaseSubdued: SEMANTIC_COLORS.shade130, + backgroundBaseDisabled: SEMANTIC_COLORS.shade125, + backgroundBasePage: SEMANTIC_COLORS.plainDark, + backgroundBasePrepend: SEMANTIC_COLORS.shade125, + + backgroundBaseHover: SEMANTIC_COLORS.shade130, + backgroundBaseSelect: SEMANTIC_COLORS.primary130, + backgroundBaseOverlay: PRIMITIVE_COLORS.transparentBlack['60'], + + backgroundLightPrimary: SEMANTIC_COLORS.primary120, + backgroundLightAccent: SEMANTIC_COLORS.accent120, + backgroundLightAccentSecondary: SEMANTIC_COLORS.accentSecondary120, + backgroundLightSuccess: SEMANTIC_COLORS.success120, + backgroundLightWarning: SEMANTIC_COLORS.warning120, + backgroundLightDanger: SEMANTIC_COLORS.danger120, + backgroundLightText: SEMANTIC_COLORS.shade120, + + backgroundFilledPrimary: SEMANTIC_COLORS.primary70, + backgroundFilledAccent: SEMANTIC_COLORS.accent70, + backgroundFilledAccentSecondary: SEMANTIC_COLORS.accentSecondary70, + backgroundFilledSuccess: SEMANTIC_COLORS.success70, + backgroundFilledWarning: SEMANTIC_COLORS.warning40, + backgroundFilledDanger: SEMANTIC_COLORS.danger70, + backgroundFilledText: SEMANTIC_COLORS.shade70, +}; + +/** + * NOTE: temp values for migration + * TODO: align values for previously transparent backgrounds + */ +export const dark_transparent_background_colors: _EuiThemeTransparentBackgroundColors = + { + backgroundTransparent: 'transparent', + backgroundTransparentPrimary: dark_background_colors.backgroundBasePrimary, + backgroundTransparentAccent: dark_background_colors.backgroundBaseAccent, + backgroundTransparentAccentSecondary: + dark_background_colors.backgroundBaseAccent, + backgroundTransparentSuccess: dark_background_colors.backgroundBaseSuccess, + backgroundTransparentWarning: dark_background_colors.backgroundBaseWarning, + backgroundTransparentDanger: dark_background_colors.backgroundBaseDanger, + backgroundTransparentSubdued: dark_background_colors.backgroundBaseSubdued, + backgroundTransparentPlain: dark_background_colors.backgroundBasePlain, + }; + +export const dark_border_colors: _EuiThemeBorderColors = { + borderBasePrimary: SEMANTIC_COLORS.primary60, + borderBaseAccent: SEMANTIC_COLORS.accent60, + borderBaseAccentSecondary: SEMANTIC_COLORS.accentSecondary60, + borderBaseSuccess: SEMANTIC_COLORS.success60, + borderBaseWarning: SEMANTIC_COLORS.warning40, + borderBaseDanger: SEMANTIC_COLORS.danger60, + borderBasePlain: SEMANTIC_COLORS.shade110, + borderBaseSubdued: SEMANTIC_COLORS.shade120, + borderBaseDisabled: SEMANTIC_COLORS.shade110, + borderBaseFloating: SEMANTIC_COLORS.shade120, +}; + +export const dark_special_colors: _EuiThemeSpecialColors = { + body: SEMANTIC_COLORS.plainDark, + highlight: SEMANTIC_COLORS.primary100, + disabled: SEMANTIC_COLORS.shade70, + disabledText: SEMANTIC_COLORS.shade120, + shadow: PRIMITIVE_COLORS.black, +}; + +export const dark_colors: _EuiThemeColorsMode = { + ...dark_brand_colors, + ...dark_shades, + ...dark_special_colors, + ...dark_brand_text_colors, + ...dark_text_colors, + ...dark_background_colors, + ...dark_transparent_background_colors, + ...dark_border_colors, +}; diff --git a/packages/eui-theme-borealis/src/variables/colors/_colors_light.scss b/packages/eui-theme-borealis/src/variables/colors/_colors_light.scss new file mode 100644 index 00000000000..9e398487594 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_colors_light.scss @@ -0,0 +1,113 @@ +// stylelint-disable color-no-hex + +// This extra import allows any variables that are created via functions to work when loaded into JS +@import 'node_modules/@elastic/eui-theme-common/src/global_styling/functions/index'; + +@import 'semantic_colors'; + +// These colors stay the same no matter the theme +// @deprecated +$euiColorGhost: $euiColorWhite !default; +$euiColorInk: $euiColorBlack !default; + +$euiColorPlainLight: $euiColorWhite; +$euiColorPlainDark: $euiColorMutedBlack; + +// Brand +$euiColorPrimary: $euiColorPrimary90 !default; +$euiColorAccent: $euiColorAccent90 !default; +$euiColorAccentSecondary: $euiColorAccentSecondary90 !default; +$euiColorSuccess: $euiColorSuccess90 !default; +$euiColorWarning: $euiColorWarning40 !default; +$euiColorDanger: $euiColorDanger90 !default; + +// Shades +$euiColorEmptyShade: $euiColorPlainLight !default; +$euiColorLightestShade: $euiColorShade20 !default; +$euiColorLightShade: $euiColorShade30 !default; +$euiColorMediumShade: $euiColorShade60 !default; +$euiColorDarkShade: $euiColorShade90 !default; +$euiColorDarkestShade: $euiColorShade120 !default; +$euiColorFullShade: $euiColorPlainLight !default; + +// Backgrounds +$euiPageBackgroundColor: $euiColorShade10 !default; // deprecated +$euiColorHighlight: $euiColorPrimary10 !default; + +$euiColorBackgroundBasePrimary: $euiColorPrimary10 !default; +$euiColorBackgroundBaseAccent: $euiColorAccent10 !default; +$euiColorBackgroundBaseAccentSecondary: $euiColorAccentSecondary10 !default; +$euiColorBackgroundBaseSuccess: $euiColorSuccess10 !default; +$euiColorBackgroundBaseWarning: $euiColorWarning10 !default; +$euiColorBackgroundBaseDanger: $euiColorDanger10 !default; +$euiColorBackgroundBasePlain: $euiColorPlainLight !default; +$euiColorBackgroundBaseSubdued: $euiColorShade10 !default; +$euiColorBackgroundBaseDisabled: $euiColorShade15 !default; +$euiColorBackgroundBasePage: $euiColorShade10 !default; +$euiColorBackgroundBasePrepend: $euiColorShade15 !default; + +$euiColorBackgroundBaseHover: $euiColorShade15 !default; +$euiColorBackgroundBaseSelect: $euiColorPrimary10 !default; +$euiColorBackgroundBaseOverlay: $euiColorTransparentBlack60 !default; + +$euiColorBackgroundLightPrimary: $euiColorPrimary20 !default; +$euiColorBackgroundLightAccent: $euiColorAccent20 !default; +$euiColorBackgroundLightAccentSecondary: $euiColorAccentSecondary20 !default; +$euiColorBackgroundLightSuccess: $euiColorSuccess20 !default; +$euiColorBackgroundLightWarning: $euiColorWarning20 !default; +$euiColorBackgroundLightDanger: $euiColorDanger20 !default; +$euiColorBackgroundLightText: $euiColorShade20 !default; + +$euiColorBackgroundFilledPrimary: $euiColorPrimary90 !default; +$euiColorBackgroundFilledAccent: $euiColorAccent90 !default; +$euiColorBackgroundFilledAccentSecondary: $euiColorAccentSecondary90 !default; +$euiColorBackgroundFilledSuccess: $euiColorSuccess90 !default; +$euiColorBackgroundFilledWarning: $euiColorWarning90 !default; +$euiColorBackgroundFilledDanger: $euiColorDanger90 !default; +$euiColorBackgroundFilledText: $euiColorShade90 !default; + +// Texts (legacy) +$euiTextColor: $euiColorShade135 !default; +$euiTitleColor: $euiColorShade140 !default; +$euiTextSubduedColor: $euiColorShade90 !default; +$euiColorDisabled: $euiColorBackgroundBaseDisabled !default; +$euiColorDisabledText: $euiColorShade70 !default; +$euiLinkColor: $euiColorPrimary100 !default; + +// Texts +$euiColorTextParagraph: $euiColorShade135 !default; +$euiColorTextHeading: $euiColorShade140 !default; +$euiColorTextSubdued: $euiColorShade90 !default; +$euiColorTextDisabled: $euiColorShade70 !default; +$euiColorTextInverse: $euiColorPlainLight !default; + +// Brand texts (legacy) +$euiColorPrimaryText: $euiColorPrimary100 !default; +$euiColorAccentText: $euiColorAccent100 !default; +$euiColorSuccessText: $euiColorSuccess100 !default; +$euiColorWarningText: $euiColorWarning100 !default; +$euiColorDangerText: $euiColorDanger100 !default; + +// Brand texts +$euiColorTextPrimary: $euiColorPrimary100 !default; +$euiColorTextAccent: $euiColorAccent100 !default; +$euiColorTextAccentSecondary: $euiColorAccentSecondary100 !default; +$euiColorTextSuccess: $euiColorSuccess100 !default; +$euiColorTextWarning: $euiColorWarning100 !default; +$euiColorTextDanger: $euiColorDanger100 !default; + +// Borders +$borderBasePrimary: $euiColorPrimary100; +$borderBaseAccent: $euiColorAccent100; +$borderBaseAccentSecondary: $euiColorAccentSecondary100; +$borderBaseSuccess: $euiColorSuccess100; +$borderBaseWarning: $euiColorWarning100; +$borderBaseDanger: $euiColorDanger100; +$borderBasePlain: $euiColorShade30; +$borderBaseSubdued: $euiColorShade20; +$borderBaseDisabled: $euiColorShade30; +$borderBaseFloating: $euiColorTransparent; + +// Charts +$euiColorChartLines: $euiColorShade30 !default; +$euiColorChartBand: $euiColorShade10 !default; diff --git a/packages/eui-theme-borealis/src/variables/colors/_colors_light.ts b/packages/eui-theme-borealis/src/variables/colors/_colors_light.ts new file mode 100644 index 00000000000..8fd70b584e0 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_colors_light.ts @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + type _EuiThemeBrandColors, + type _EuiThemeBrandTextColors, + type _EuiThemeShadeColors, + type _EuiThemeSpecialColors, + type _EuiThemeTextColors, + type _EuiThemeColorsMode, + _EuiThemeBackgroundColors, + _EuiThemeBorderColors, + _EuiThemeTransparentBackgroundColors, +} from '@elastic/eui-theme-common'; + +import { PRIMITIVE_COLORS } from './_primitive_colors'; +import { SEMANTIC_COLORS } from './_semantic_colors'; + +/* + * LIGHT THEME + */ + +export const brand_colors: _EuiThemeBrandColors = { + primary: SEMANTIC_COLORS.primary90, + accent: SEMANTIC_COLORS.accent90, + accentSecondary: SEMANTIC_COLORS.accentSecondary90, + success: SEMANTIC_COLORS.success90, + warning: SEMANTIC_COLORS.warning40, + danger: SEMANTIC_COLORS.danger90, +}; + +export const brand_text_colors: _EuiThemeBrandTextColors = { + /* Legacy colors */ + primaryText: SEMANTIC_COLORS.primary100, + accentText: SEMANTIC_COLORS.accent100, + successText: SEMANTIC_COLORS.success100, + warningText: SEMANTIC_COLORS.warning100, + dangerText: SEMANTIC_COLORS.danger100, + + /* New colors */ + textPrimary: SEMANTIC_COLORS.primary100, + textAccent: SEMANTIC_COLORS.accent100, + textAccentSecondary: SEMANTIC_COLORS.accentSecondary100, + textSuccess: SEMANTIC_COLORS.success100, + textWarning: SEMANTIC_COLORS.warning100, + textDanger: SEMANTIC_COLORS.danger100, +}; + +export const text_colors: _EuiThemeTextColors = { + /* Legacy colors */ + text: SEMANTIC_COLORS.shade135, + title: SEMANTIC_COLORS.shade140, + subduedText: SEMANTIC_COLORS.shade90, + link: SEMANTIC_COLORS.primary100, + + /* New colors */ + textParagraph: SEMANTIC_COLORS.shade135, + textHeading: SEMANTIC_COLORS.shade140, + textSubdued: SEMANTIC_COLORS.shade90, + textDisabled: SEMANTIC_COLORS.shade70, + textInverse: SEMANTIC_COLORS.plainLight, +}; + +export const shade_colors: _EuiThemeShadeColors = { + emptyShade: SEMANTIC_COLORS.plainLight, + lightestShade: SEMANTIC_COLORS.shade20, + lightShade: SEMANTIC_COLORS.shade30, + mediumShade: SEMANTIC_COLORS.shade60, + darkShade: SEMANTIC_COLORS.shade90, + darkestShade: SEMANTIC_COLORS.shade120, + fullShade: SEMANTIC_COLORS.plainDark, +}; + +export const background_colors: _EuiThemeBackgroundColors = { + backgroundBasePrimary: SEMANTIC_COLORS.primary10, + backgroundBaseAccent: SEMANTIC_COLORS.accent10, + backgroundBaseAccentSecondary: SEMANTIC_COLORS.accentSecondary10, + backgroundBaseSuccess: SEMANTIC_COLORS.success10, + backgroundBaseWarning: SEMANTIC_COLORS.warning10, + backgroundBaseDanger: SEMANTIC_COLORS.danger10, + backgroundBasePlain: SEMANTIC_COLORS.plainLight, + backgroundBaseSubdued: SEMANTIC_COLORS.shade10, + backgroundBaseDisabled: SEMANTIC_COLORS.shade15, + backgroundBasePage: SEMANTIC_COLORS.shade10, + backgroundBasePrepend: SEMANTIC_COLORS.shade15, + + backgroundBaseHover: SEMANTIC_COLORS.shade15, + backgroundBaseSelect: SEMANTIC_COLORS.primary10, + backgroundBaseOverlay: PRIMITIVE_COLORS.transparentBlack['60'], + + backgroundLightPrimary: SEMANTIC_COLORS.primary20, + backgroundLightAccent: SEMANTIC_COLORS.accent20, + backgroundLightAccentSecondary: SEMANTIC_COLORS.accentSecondary20, + backgroundLightSuccess: SEMANTIC_COLORS.success20, + backgroundLightWarning: SEMANTIC_COLORS.warning20, + backgroundLightDanger: SEMANTIC_COLORS.danger20, + backgroundLightText: SEMANTIC_COLORS.shade20, + + backgroundFilledPrimary: SEMANTIC_COLORS.primary90, + backgroundFilledAccent: SEMANTIC_COLORS.accent90, + backgroundFilledAccentSecondary: SEMANTIC_COLORS.accentSecondary90, + backgroundFilledSuccess: SEMANTIC_COLORS.success90, + backgroundFilledWarning: SEMANTIC_COLORS.warning40, + backgroundFilledDanger: SEMANTIC_COLORS.danger90, + backgroundFilledText: SEMANTIC_COLORS.shade90, +}; + +/** + * NOTE: temp values for migration + * TODO: align values for previously transparent backgrounds + */ +export const transparent_background_colors: _EuiThemeTransparentBackgroundColors = + { + backgroundTransparent: 'transparent', + backgroundTransparentPrimary: background_colors.backgroundBasePrimary, + backgroundTransparentAccent: background_colors.backgroundBaseAccent, + backgroundTransparentAccentSecondary: + background_colors.backgroundBaseAccentSecondary, + backgroundTransparentSuccess: background_colors.backgroundBaseSuccess, + backgroundTransparentWarning: background_colors.backgroundBaseWarning, + backgroundTransparentDanger: background_colors.backgroundBaseDanger, + backgroundTransparentSubdued: SEMANTIC_COLORS.shade15, + backgroundTransparentPlain: SEMANTIC_COLORS.shade15, + }; + +export const border_colors: _EuiThemeBorderColors = { + borderBasePrimary: SEMANTIC_COLORS.primary100, + borderBaseAccent: SEMANTIC_COLORS.accent100, + borderBaseAccentSecondary: SEMANTIC_COLORS.accentSecondary100, + borderBaseSuccess: SEMANTIC_COLORS.success100, + borderBaseWarning: SEMANTIC_COLORS.warning100, + borderBaseDanger: SEMANTIC_COLORS.danger100, + borderBasePlain: SEMANTIC_COLORS.shade30, + borderBaseSubdued: SEMANTIC_COLORS.shade20, + borderBaseDisabled: SEMANTIC_COLORS.shade30, + borderBaseFloating: PRIMITIVE_COLORS.transparent, +}; + +export const special_colors: _EuiThemeSpecialColors = { + body: SEMANTIC_COLORS.shade10, + highlight: SEMANTIC_COLORS.primary10, + disabled: SEMANTIC_COLORS.shade20, + disabledText: SEMANTIC_COLORS.shade80, + shadow: PRIMITIVE_COLORS.black, +}; + +export const light_colors: _EuiThemeColorsMode = { + ...brand_colors, + ...shade_colors, + ...special_colors, + ...brand_text_colors, + ...text_colors, + ...background_colors, + ...transparent_background_colors, + ...border_colors, +}; diff --git a/packages/eui-theme-borealis/src/variables/colors/_primitive_colors.ts b/packages/eui-theme-borealis/src/variables/colors/_primitive_colors.ts new file mode 100644 index 00000000000..40942b3f32a --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_primitive_colors.ts @@ -0,0 +1,190 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const PRIMITIVE_COLORS = { + white: '#FFFFFF', + black: '#000000', + mutedBlack: '#0E0F12', + blueBlack: '#050F21', + transparent: 'transparent', + transparentBlack: { + 60: 'rgba(0, 0, 0, 0.6)', + }, + mutedGrey: { + 10: '#F6F9FC', + 15: '#EDEFF3', + 20: '#E3E6EB', + 25: '#D9DDE3', + 30: '#D0D4DA', + 35: '#C6CBD2', + 40: '#BDC2CA', + 45: '#B3B9C2', + 50: '#AAB0BA', + 55: '#A1A8B2', + 60: '#989FAA', + 65: '#8F96A2', + 70: '#868E9A', + 75: '#7E8691', + 80: '#767D89', + 85: '#6E7581', + 90: '#666D78', + 95: '#5D6570', + 100: '#555C67', + 105: '#4D545E', + 110: '#464C56', + 115: '#3F444D', + 120: '#373D45', + 125: '#30353C', + 130: '#292E34', + 135: '#23262C', + 140: '#1C1F24', + 145: '#16181D', + }, + blueGrey: { + 10: '#F6F9FC', + 15: '#EBEFF6', + 20: '#E0E6F1', + 25: '#D4DDEB', + 30: '#C9D4E6', + 35: '#BECCE0', + 40: '#B3C3DA', + 45: '#A9BAD5', + 50: '#9EB1CF', + 55: '#93A8C9', + 60: '#89A0C4', + 65: '#7E97BE', + 70: '#748FB8', + 75: '#6C86AF', + 80: '#647EA7', + 85: '#5C759E', + 90: '#546D95', + 95: '#4C658C', + 100: '#445C83', + 105: '#3D5479', + 110: '#364C6F', + 115: '#2F4466', + 120: '#283C5C', + 125: '#223553', + 130: '#1B2D49', + 135: '#152640', + 140: '#0F1F38', + 145: '#09182F', + }, + blue: { + 10: '#E5F1FF', + 20: '#D2E7FF', + 30: '#B4D5FF', + 40: '#96C3FF', + 50: '#78B0FF', + 60: '#599DFF', + 70: '#3788FF', + 80: '#2476F0', + 90: '#0B64DD', + 100: '#004FC7', + 110: '#00419E', + 120: '#043376', + 130: '#092551', + 140: '#09182F', + }, + teal: { + 10: '#D9FDFB', + 20: '#C0F1EE', + 30: '#93E5E0', + 40: '#5DD8D2', + 50: '#00CBC5', + 60: '#00BEB8', + 70: '#00B0AA', + 80: '#009E99', + 90: '#008C88', + 100: '#007775', + 110: '#00605D', + 120: '#004947', + 130: '#003432', + 140: '#001F1E', + }, + pink: { + 10: '#FFEBF5', + 20: '#FFD9E7', + 30: '#FFBED5', + 40: '#FBA3C4', + 50: '#F588B3', + 60: '#ED6BA2', + 70: '#E54A91', + 80: '#D13680', + 90: '#BD1F70', + 100: '#A6005E', + 110: '#85044B', + 120: '#650D3A', + 130: '#460F29', + 140: '#290D19', + }, + green: { + 10: '#D9FDEA', + 20: '#CAF1DB', + 30: '#A6E4C2', + 40: '#7ED8A9', + 50: '#4DCB91', + 60: '#00BD79', + 70: '#00B060', + 80: '#009E50', + 90: '#008C40', + 100: '#00782D', + 110: '#006026', + 120: '#00491F', + 130: '#003317', + 140: '#001F10', + }, + yellow: { + 10: '#FFF1CC', + 20: '#FCE8B0', + 30: '#FFD569', + 40: '#FEC514', + 50: '#F5AF00', + 60: '#ED9E00', + 70: '#DE9000', + 80: '#CC7A00', + 90: '#B06400', + 100: '#8F4F00', + 110: '#733D00', + 120: '#5C2E00', + 130: '#4A2300', + 140: '#331600', + }, + red: { + 10: '#FFE9E5', + 20: '#FFDAD5', + 30: '#FFC0B8', + 40: '#FFA59C', + 50: '#FC8A80', + 60: '#F66D64', + 70: '#EE4C48', + 80: '#DA3737', + 90: '#C61E25', + 100: '#AF000E', + 110: '#8C0210', + 120: '#6A0D10', + 130: '#4A100F', + 140: '#2B0E0C', + }, + purple: { + 10: '#F3EBFF', + 20: '#EBDFFF', + 30: '#DBC8FF', + 40: '#CCB1FE', + 50: '#BE9AFB', + 60: '#B082F7', + 70: '#A269F3', + 80: '#9157DF', + 90: '#8144CC', + 100: '#6E2EB6', + 110: '#582891', + 120: '#43226D', + 130: '#2F1A4B', + 140: '#1C122C', + }, +}; diff --git a/packages/eui-theme-borealis/src/variables/colors/_semantic_colors.scss b/packages/eui-theme-borealis/src/variables/colors/_semantic_colors.scss new file mode 100644 index 00000000000..499a4f867a5 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_semantic_colors.scss @@ -0,0 +1,152 @@ +/** + * Temp. manually created file + * TODO: automatically generate scss variable files + */ + + + $euiColorWhite: #FFF !default; + $euiColorBlack: #000 !default; + $euiColorMutedBlack: #0E0F12 !default; + $euiColorBlueBlack: #050F21 !default; + + $euiColorTransparent: 'transparent' !default; + $euiColorTransparentBlack60: rgba(0, 0, 0, 0.6) !default; + + $euiColorPlainLight: $euiColorWhite !default; + $euiColorPlainDark: $euiColorMutedBlack !default; + + $euiColorPrimary10: #F1F9FF !default; + $euiColorPrimary20: #D2E7FF !default; + $euiColorPrimary30: #B4D5FF !default; + $euiColorPrimary40: #96C3FF !default; + $euiColorPrimary50: #78B0FF !default; + $euiColorPrimary60: #599DFF !default; + $euiColorPrimary70: #3788FF !default; + $euiColorPrimary80: #2476F0 !default; + $euiColorPrimary90: #0B64DD !default; + $euiColorPrimary100: #004FC7 !default; + $euiColorPrimary110: #00419E !default; + $euiColorPrimary120: #043376 !default; + $euiColorPrimary130: #092551 !default; + $euiColorPrimary140: #09182F !default; + + $euiColorAccent10: #FFF3F9 !default; + $euiColorAccent20: #FFD9E7 !default; + $euiColorAccent30: #FFBED5 !default; + $euiColorAccent40: #FBA3C4 !default; + $euiColorAccent50: #F588B3 !default; + $euiColorAccent60: #ED6BA2 !default; + $euiColorAccent70: #E54A91 !default; + $euiColorAccent80: #D13680 !default; + $euiColorAccent90: #BD1F70 !default; + $euiColorAccent100: #A6005E !default; + $euiColorAccent110: #85044B !default; + $euiColorAccent120: #650D3A !default; + $euiColorAccent130: #460F29 !default; + $euiColorAccent140: #290D19 !default; + + $euiColorAccentSecondary10: #EAFDFC !default; + $euiColorAccentSecondary20: #C0F1EE !default; + $euiColorAccentSecondary30: #93E5E0 !default; + $euiColorAccentSecondary40: #5DD8D2 !default; + $euiColorAccentSecondary50: #00CBC5 !default; + $euiColorAccentSecondary60: #00BEB8 !default; + $euiColorAccentSecondary70: #00B0AA !default; + $euiColorAccentSecondary80: #009E99 !default; + $euiColorAccentSecondary90: #008C88 !default; + $euiColorAccentSecondary100: #007775 !default; + $euiColorAccentSecondary110: #00605D !default; + $euiColorAccentSecondary120: #004947 !default; + $euiColorAccentSecondary130: #003432 !default; + $euiColorAccentSecondary140: #001F1E !default; + + $euiColorSuccess10: #EEFDF4 !default; + $euiColorSuccess20: #CAF1DB !default; + $euiColorSuccess30: #A6E4C2 !default; + $euiColorSuccess40: #7ED8A9 !default; + $euiColorSuccess50: #4DCB91 !default; + $euiColorSuccess60: #00BD79 !default; + $euiColorSuccess70: #00B060 !default; + $euiColorSuccess80: #009E50 !default; + $euiColorSuccess90: #008C40 !default; + $euiColorSuccess100: #00782D !default; + $euiColorSuccess110: #006026 !default; + $euiColorSuccess120: #00491F !default; + $euiColorSuccess130: #003317 !default; + $euiColorSuccess140: #001F10 !default; + + $euiColorWarning10: #FEF8E7 !default; + $euiColorWarning20: #FCE8B0 !default; + $euiColorWarning30: #FFD569 !default; + $euiColorWarning40: #FEC514 !default; + $euiColorWarning50: #F5AF00 !default; + $euiColorWarning60: #ED9E00 !default; + $euiColorWarning70: #DE9000 !default; + $euiColorWarning80: #CC7A00 !default; + $euiColorWarning90: #B06400 !default; + $euiColorWarning100: #8F4F00 !default; + $euiColorWarning110: #733D00 !default; + $euiColorWarning120: #5C2E00 !default; + $euiColorWarning130: #4A2300 !default; + $euiColorWarning140: #331600 !default; + + $euiColorDanger10: #FFF4F1 !default; + $euiColorDanger20: #FFDAD5 !default; + $euiColorDanger30: #FFC0B8 !default; + $euiColorDanger40: #FFA59C !default; + $euiColorDanger50: #FC8A80 !default; + $euiColorDanger60: #F66D64 !default; + $euiColorDanger70: #EE4C48 !default; + $euiColorDanger80: #DA3737 !default; + $euiColorDanger90: #C61E25 !default; + $euiColorDanger100: #AF000E !default; + $euiColorDanger110: #8C0210 !default; + $euiColorDanger120: #6A0D10 !default; + $euiColorDanger130: #4A100F !default; + $euiColorDanger140: #2B0E0C !default; + + $euiColorAssistance10: #FAF6FF !default; + $euiColorAssistance20: #EBDFFF !default; + $euiColorAssistance30: #DBC8FF !default; + $euiColorAssistance40: #CCB1FE !default; + $euiColorAssistance50: #BE9AFB !default; + $euiColorAssistance60: #B082F7 !default; + $euiColorAssistance70: #A269F3 !default; + $euiColorAssistance80: #9157DF !default; + $euiColorAssistance90: #8144CC !default; + $euiColorAssistance100: #6E2EB6 !default; + $euiColorAssistance110: #582891 !default; + $euiColorAssistance120: #43226D !default; + $euiColorAssistance130: #2F1A4B !default; + $euiColorAssistance140: #1C122C !default; + + $euiColorShade10: #F6F9FC !default; + $euiColorShade15: #EDEFF3 !default; + $euiColorShade20: #E3E6EB !default; + $euiColorShade25: #D9DDE3 !default; + $euiColorShade30: #D0D4DA !default; + $euiColorShade35: #C6CBD2 !default; + $euiColorShade40: #BDC2CA !default; + $euiColorShade45: #B3B9C2 !default; + $euiColorShade50: #AAB0BA !default; + $euiColorShade55: #A1A8B2 !default; + $euiColorShade60: #989FAA !default; + $euiColorShade65: #8F96A2 !default; + $euiColorShade70: #868E9A !default; + $euiColorShade75: #7E8691 !default; + $euiColorShade80: #767D89 !default; + $euiColorShade85: #6E7581 !default; + $euiColorShade90: #666D78 !default; + $euiColorShade95: #5D6570 !default; + $euiColorShade100: #555C67 !default; + $euiColorShade105: #4D545E !default; + $euiColorShade110: #464C56 !default; + $euiColorShade115: #3F444D !default; + $euiColorShade120: #373D45 !default; + $euiColorShade125: #30353C !default; + $euiColorShade130: #292E34 !default; + $euiColorShade135: #23262C !default; + $euiColorShade140: #1C1F24 !default; + $euiColorShade145: #16181D !default; + + \ No newline at end of file diff --git a/packages/eui-theme-borealis/src/variables/colors/_semantic_colors.ts b/packages/eui-theme-borealis/src/variables/colors/_semantic_colors.ts new file mode 100644 index 00000000000..d340a5e0229 --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/_semantic_colors.ts @@ -0,0 +1,148 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { PRIMITIVE_COLORS } from './_primitive_colors'; + +export const SEMANTIC_COLORS = { + plainLight: PRIMITIVE_COLORS.white, + plainDark: PRIMITIVE_COLORS.blueBlack, + + primary10: PRIMITIVE_COLORS.blue['10'], + primary20: PRIMITIVE_COLORS.blue['20'], + primary30: PRIMITIVE_COLORS.blue['30'], + primary40: PRIMITIVE_COLORS.blue['40'], + primary50: PRIMITIVE_COLORS.blue['50'], + primary60: PRIMITIVE_COLORS.blue['60'], + primary70: PRIMITIVE_COLORS.blue['70'], + primary80: PRIMITIVE_COLORS.blue['80'], + primary90: PRIMITIVE_COLORS.blue['90'], + primary100: PRIMITIVE_COLORS.blue['100'], + primary110: PRIMITIVE_COLORS.blue['110'], + primary120: PRIMITIVE_COLORS.blue['120'], + primary130: PRIMITIVE_COLORS.blue['130'], + primary140: PRIMITIVE_COLORS.blue['140'], + + accent10: PRIMITIVE_COLORS.pink['10'], + accent20: PRIMITIVE_COLORS.pink['20'], + accent30: PRIMITIVE_COLORS.pink['30'], + accent40: PRIMITIVE_COLORS.pink['40'], + accent50: PRIMITIVE_COLORS.pink['50'], + accent60: PRIMITIVE_COLORS.pink['60'], + accent70: PRIMITIVE_COLORS.pink['70'], + accent80: PRIMITIVE_COLORS.pink['80'], + accent90: PRIMITIVE_COLORS.pink['90'], + accent100: PRIMITIVE_COLORS.pink['100'], + accent110: PRIMITIVE_COLORS.pink['110'], + accent120: PRIMITIVE_COLORS.pink['120'], + accent130: PRIMITIVE_COLORS.pink['130'], + accent140: PRIMITIVE_COLORS.pink['140'], + + accentSecondary10: PRIMITIVE_COLORS.teal['10'], + accentSecondary20: PRIMITIVE_COLORS.teal['20'], + accentSecondary30: PRIMITIVE_COLORS.teal['30'], + accentSecondary40: PRIMITIVE_COLORS.teal['40'], + accentSecondary50: PRIMITIVE_COLORS.teal['50'], + accentSecondary60: PRIMITIVE_COLORS.teal['60'], + accentSecondary70: PRIMITIVE_COLORS.teal['70'], + accentSecondary80: PRIMITIVE_COLORS.teal['80'], + accentSecondary90: PRIMITIVE_COLORS.teal['90'], + accentSecondary100: PRIMITIVE_COLORS.teal['100'], + accentSecondary110: PRIMITIVE_COLORS.teal['110'], + accentSecondary120: PRIMITIVE_COLORS.teal['120'], + accentSecondary130: PRIMITIVE_COLORS.teal['130'], + accentSecondary140: PRIMITIVE_COLORS.teal['140'], + + success10: PRIMITIVE_COLORS.green['10'], + success20: PRIMITIVE_COLORS.green['20'], + success30: PRIMITIVE_COLORS.green['30'], + success40: PRIMITIVE_COLORS.green['40'], + success50: PRIMITIVE_COLORS.green['50'], + success60: PRIMITIVE_COLORS.green['60'], + success70: PRIMITIVE_COLORS.green['70'], + success80: PRIMITIVE_COLORS.green['80'], + success90: PRIMITIVE_COLORS.green['90'], + success100: PRIMITIVE_COLORS.green['100'], + success110: PRIMITIVE_COLORS.green['110'], + success120: PRIMITIVE_COLORS.green['120'], + success130: PRIMITIVE_COLORS.green['130'], + success140: PRIMITIVE_COLORS.green['140'], + + warning10: PRIMITIVE_COLORS.yellow['10'], + warning20: PRIMITIVE_COLORS.yellow['20'], + warning30: PRIMITIVE_COLORS.yellow['30'], + warning40: PRIMITIVE_COLORS.yellow['40'], + warning50: PRIMITIVE_COLORS.yellow['50'], + warning60: PRIMITIVE_COLORS.yellow['60'], + warning70: PRIMITIVE_COLORS.yellow['70'], + warning80: PRIMITIVE_COLORS.yellow['80'], + warning90: PRIMITIVE_COLORS.yellow['90'], + warning100: PRIMITIVE_COLORS.yellow['100'], + warning110: PRIMITIVE_COLORS.yellow['110'], + warning120: PRIMITIVE_COLORS.yellow['120'], + warning130: PRIMITIVE_COLORS.yellow['130'], + warning140: PRIMITIVE_COLORS.yellow['140'], + + danger10: PRIMITIVE_COLORS.red['10'], + danger20: PRIMITIVE_COLORS.red['20'], + danger30: PRIMITIVE_COLORS.red['30'], + danger40: PRIMITIVE_COLORS.red['40'], + danger50: PRIMITIVE_COLORS.red['50'], + danger60: PRIMITIVE_COLORS.red['60'], + danger70: PRIMITIVE_COLORS.red['70'], + danger80: PRIMITIVE_COLORS.red['80'], + danger90: PRIMITIVE_COLORS.red['90'], + danger100: PRIMITIVE_COLORS.red['100'], + danger110: PRIMITIVE_COLORS.red['110'], + danger120: PRIMITIVE_COLORS.red['120'], + danger130: PRIMITIVE_COLORS.red['130'], + danger140: PRIMITIVE_COLORS.red['140'], + + assistance10: PRIMITIVE_COLORS.purple['10'], + assistance20: PRIMITIVE_COLORS.purple['20'], + assistance30: PRIMITIVE_COLORS.purple['30'], + assistance40: PRIMITIVE_COLORS.purple['40'], + assistance50: PRIMITIVE_COLORS.purple['50'], + assistance60: PRIMITIVE_COLORS.purple['60'], + assistance70: PRIMITIVE_COLORS.purple['70'], + assistance80: PRIMITIVE_COLORS.purple['80'], + assistance90: PRIMITIVE_COLORS.purple['90'], + assistance100: PRIMITIVE_COLORS.purple['100'], + assistance110: PRIMITIVE_COLORS.purple['110'], + assistance120: PRIMITIVE_COLORS.purple['120'], + assistance130: PRIMITIVE_COLORS.purple['130'], + assistance140: PRIMITIVE_COLORS.purple['140'], + + shade10: PRIMITIVE_COLORS.blueGrey['10'], + shade15: PRIMITIVE_COLORS.blueGrey['15'], + shade20: PRIMITIVE_COLORS.blueGrey['20'], + shade25: PRIMITIVE_COLORS.blueGrey['25'], + shade30: PRIMITIVE_COLORS.blueGrey['30'], + shade35: PRIMITIVE_COLORS.blueGrey['35'], + shade40: PRIMITIVE_COLORS.blueGrey['40'], + shade45: PRIMITIVE_COLORS.blueGrey['45'], + shade50: PRIMITIVE_COLORS.blueGrey['50'], + shade55: PRIMITIVE_COLORS.blueGrey['55'], + shade60: PRIMITIVE_COLORS.blueGrey['60'], + shade65: PRIMITIVE_COLORS.blueGrey['65'], + shade70: PRIMITIVE_COLORS.blueGrey['70'], + shade75: PRIMITIVE_COLORS.blueGrey['75'], + shade80: PRIMITIVE_COLORS.blueGrey['80'], + shade85: PRIMITIVE_COLORS.blueGrey['85'], + shade90: PRIMITIVE_COLORS.blueGrey['90'], + shade95: PRIMITIVE_COLORS.blueGrey['95'], + shade100: PRIMITIVE_COLORS.blueGrey['100'], + shade105: PRIMITIVE_COLORS.blueGrey['105'], + shade110: PRIMITIVE_COLORS.blueGrey['110'], + shade115: PRIMITIVE_COLORS.blueGrey['115'], + shade120: PRIMITIVE_COLORS.blueGrey['120'], + shade125: PRIMITIVE_COLORS.blueGrey['125'], + shade130: PRIMITIVE_COLORS.blueGrey['130'], + shade135: PRIMITIVE_COLORS.blueGrey['135'], + shade140: PRIMITIVE_COLORS.blueGrey['140'], + shade145: PRIMITIVE_COLORS.blueGrey['145'], +}; diff --git a/packages/eui-theme-borealis/src/variables/colors/index.ts b/packages/eui-theme-borealis/src/variables/colors/index.ts new file mode 100644 index 00000000000..5bb5e522e3c --- /dev/null +++ b/packages/eui-theme-borealis/src/variables/colors/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { _EuiThemeColors } from '@elastic/eui-theme-common'; + +import { SEMANTIC_COLORS } from './_semantic_colors'; +import { light_colors } from './_colors_light'; +import { dark_colors } from './_colors_dark'; + +export const colors: _EuiThemeColors = { + ghost: SEMANTIC_COLORS.plainLight, + ink: SEMANTIC_COLORS.plainDark, + plainLight: SEMANTIC_COLORS.plainLight, + plainDark: SEMANTIC_COLORS.plainDark, + LIGHT: light_colors, + DARK: dark_colors, +}; diff --git a/packages/eui-theme-borealis/tsconfig.json b/packages/eui-theme-borealis/tsconfig.json new file mode 100644 index 00000000000..4415cdcb934 --- /dev/null +++ b/packages/eui-theme-borealis/tsconfig.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib", + "target": "ES2020", + "lib": ["ESNext", "DOM"], + "moduleResolution": "Node", + "declaration": true, + "sourceMap": true, + "noEmitHelpers": true, + "incremental": true, + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "tsBuildInfoFile": "lib/.tsbuildinfo" + }, + "include": ["src"], + "exclude": ["node_modules"], + } \ No newline at end of file diff --git a/packages/eui-theme-common/.babelrc.js b/packages/eui-theme-common/.babelrc.js new file mode 100644 index 00000000000..0fc74dd1fe9 --- /dev/null +++ b/packages/eui-theme-common/.babelrc.js @@ -0,0 +1,27 @@ +module.exports = { + // We need to preserve comments as they are used by webpack for + // naming chunks during code-splitting. The compression step during + // bundling will remove them later. + comments: true, + + presets: [ + [ + '@babel/env', + { + // `targets` property set via `.browserslistrc` + useBuiltIns: process.env.NO_COREJS_POLYFILL ? false : 'usage', + corejs: !process.env.NO_COREJS_POLYFILL ? '3.6' : undefined, + modules: process.env.BABEL_MODULES + ? process.env.BABEL_MODULES === 'false' + ? false + : process.env.BABEL_MODULES + : 'commonjs', // babel's default is commonjs + }, + ], + ['@babel/react', { runtime: 'classic' }], + [ + '@babel/typescript', + { isTSX: true, allExtensions: true, allowDeclareFields: true }, + ], + ], +}; diff --git a/packages/eui-theme-common/.eslintignore b/packages/eui-theme-common/.eslintignore new file mode 100644 index 00000000000..c6be7e9a761 --- /dev/null +++ b/packages/eui-theme-common/.eslintignore @@ -0,0 +1,10 @@ +dist +node_modules +lib +types +**/*.d.ts +package.json +scripts +.eslintrc.js +babel.config.js +jest.config.js diff --git a/packages/eui-theme-common/.eslintplugin.js b/packages/eui-theme-common/.eslintplugin.js new file mode 100644 index 00000000000..25ddbe3db65 --- /dev/null +++ b/packages/eui-theme-common/.eslintplugin.js @@ -0,0 +1,3 @@ +exports.rules = { + 'require-license-header': require('./scripts/eslint-plugin-local/require_license_header.js'), +}; diff --git a/packages/eui-theme-common/.eslintrc.js b/packages/eui-theme-common/.eslintrc.js new file mode 100644 index 00000000000..e33853c0255 --- /dev/null +++ b/packages/eui-theme-common/.eslintrc.js @@ -0,0 +1,114 @@ +const SSPL_ELASTIC_2_0_LICENSE_HEADER = ` +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +`; + +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + project: ['./tsconfig.json'], + ecmaFeatures: { + jsx: true, + }, + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.ts', '.tsx', '.js', '.json'], + }, + }, + }, + extends: [ + 'plugin:@typescript-eslint/recommended', + // Prettier options need to come last, in order to override other style rules + 'plugin:prettier/recommended', + ], + plugins: ['local', 'import'], + rules: { + 'block-scoped-var': 'error', + camelcase: 'off', + 'dot-notation': ['error', { allowKeywords: true }], + eqeqeq: ['error', 'always', { null: 'ignore' }], + 'guard-for-in': 'error', + 'new-cap': ['error', { capIsNewExceptions: ['Private'] }], + 'no-caller': 'error', + 'no-const-assign': 'error', + 'no-debugger': 'error', + 'no-empty': ['error', { allowEmptyCatch: true }], + 'no-eval': 'error', + 'no-extend-native': 'error', + 'no-global-assign': 'error', + 'no-loop-func': 'error', + 'no-restricted-globals': ['error', 'context'], + 'no-script-url': 'error', + 'no-sequences': 'error', + 'no-var': 'error', + 'no-with': 'error', + 'prefer-const': 'error', + 'prefer-template': 'error', + strict: ['error', 'never'], + 'valid-typeof': 'error', + + 'local/require-license-header': [ + 'warn', + { + license: SSPL_ELASTIC_2_0_LICENSE_HEADER, + }, + ], + + 'import/no-unresolved': ['error', { amd: true, commonjs: true }], + 'import/namespace': 'error', + 'import/default': 'error', + 'import/export': 'error', + 'import/no-named-as-default': 'error', + 'import/no-named-as-default-member': 'error', + 'import/no-duplicates': 'error', + + '@typescript-eslint/array-type': ['error', { default: 'array-simple' }], + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-member-accessibility': 'off', + '@typescript-eslint/indent': 'off', + '@typescript-eslint/ban-tslint-comment': 'error', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-parameter-properties': 'off', + '@typescript-eslint/no-triple-slash-reference': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', ignoreRestSiblings: true }, + ], + '@typescript-eslint/no-use-before-define': 'off', + '@typescript-eslint/no-empty-function': 'off', + // It"s all very well saying that some types are trivially inferrable, + // but being explicit is still clearer. + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/naming-convention': 'off', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-ignore': 'allow-with-description', + 'ts-expect-error': 'allow-with-description', + }, + ], + '@typescript-eslint/consistent-type-exports': [ + 'error', + { fixMixedExportsWithInlineTypeSpecifier: false }, + ], + }, + overrides: [ + { + files: ['*.d.ts'], + rules: { + 'react/prefer-es6-class': 'off', + }, + }, + ], +}; diff --git a/packages/eui-theme-common/.gitignore b/packages/eui-theme-common/.gitignore new file mode 100644 index 00000000000..4450fe8d868 --- /dev/null +++ b/packages/eui-theme-common/.gitignore @@ -0,0 +1,11 @@ +# Dependencies +/node_modules + +# Production +/lib + +yarn-debug.log* +yarn-error.log* + +# Build-related files +.eslintcache \ No newline at end of file diff --git a/packages/eui-theme-common/.prettierrc b/packages/eui-theme-common/.prettierrc new file mode 100644 index 00000000000..b2f0fa8f00e --- /dev/null +++ b/packages/eui-theme-common/.prettierrc @@ -0,0 +1,7 @@ +{ + "parser": "typescript", + "printWidth": 80, + "semi": true, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/packages/eui-theme-common/.stylelintrc.js b/packages/eui-theme-common/.stylelintrc.js new file mode 100644 index 00000000000..42adc41f28c --- /dev/null +++ b/packages/eui-theme-common/.stylelintrc.js @@ -0,0 +1,163 @@ +const camelCaseRegex = '^[a-z][\\w-]*$'; // Note: also allows `_` as part of BEM naming +const camelCaseValueRegex = '/^[a-z][\\w.]*$/'; // Note: also allows `.` for JS objects +const cssInJsVarRegex = '/\\${[a-zA-Z]+/'; + +module.exports = { + // @see https://stylelint.io/user-guide/rules + rules: { + // Enforce camelCase naming + 'selector-class-pattern': camelCaseRegex, + 'keyframes-name-pattern': camelCaseRegex, + 'custom-property-pattern': camelCaseRegex, + + // Opinionated rules + 'declaration-no-important': true, + 'max-nesting-depth': [ + 2, + { + ignore: ['blockless-at-rules', 'pseudo-classes'], + }, + ], + 'block-no-empty': true, + 'selector-no-qualifying-type': [ + true, + { + ignore: ['attribute'], // Allows input[type=search] + }, + ], + + // Non-Prettier newline rules + // Put a line-break between sections of CSS, but allow quick one-liners for legibility + 'rule-empty-line-before': [ + 'always-multi-line', + { + ignore: ['first-nested', 'after-comment'], + }, + ], + 'comment-empty-line-before': null, + 'declaration-empty-line-before': null, + + // Value preferences + 'number-max-precision': null, + // Attempt to catch/flag non-variable color values + 'color-named': 'never', + 'color-no-hex': true, + // Prefer lowercase values, except for font names and currentColor + 'value-keyword-case': [ + 'lower', + { + ignoreProperties: ['font-family', '/^\\$eui[\\w]+/'], // Allow fonts and Sass variables + ignoreKeywords: ['currentColor'], + }, + ], + 'declaration-block-no-duplicate-properties': [ + true, + { + ignore: ['consecutive-duplicates'], // We occasionally use duplicate property values for cross-browser fallbacks + }, + ], + + // TODO: It may be worth investigating and updating these rules to their more modern counterparts + 'color-function-notation': 'legacy', + 'alpha-value-notation': 'number', + + // Disable various opinionated extended stylelint rules that EUI has not previously enforced + 'no-descending-specificity': null, + 'keyframe-selector-notation': null, + 'declaration-block-no-redundant-longhand-properties': null, + }, + overrides: [ + { + // TODO: Remove Sass-specific config & rules once we're completely off Sass + files: ['**/*.scss'], + ignoreFiles: [ + 'generator-eui/**/*.scss', + 'src/global_styling/react_date_picker/**/*.scss', + 'src/themes/amsterdam/global_styling/react_date_picker/**/*.scss', + 'src/components/date_picker/react-datepicker/**/*.scss', + ], + extends: [ + 'stylelint-config-standard-scss', + 'stylelint-config-prettier-scss', + ], + // @see https://github.com/stylelint-scss/stylelint-scss#list-of-rules + rules: { + // Casing + 'scss/dollar-variable-pattern': camelCaseRegex, + 'scss/at-mixin-pattern': camelCaseRegex, + 'scss/at-function-pattern': camelCaseRegex, + 'function-name-case': [ + 'lower', + { + ignoreFunctions: [`/${camelCaseRegex}/`, 'MIN'], + }, + ], + + // Whitespace/newlines + 'scss/at-if-closing-brace-space-after': 'always-intermediate', + 'scss/operator-no-unspaced': true, + + // Formatting rules deprecated as of v15 - keep them in Sass styles just in case until end of migration + // @see https://github.com/stylelint/stylelint/blob/main/docs/user-guide/rules.md#deprecated + 'color-hex-case': 'upper', + 'string-quotes': 'single', + // 2 spaces for indentation + indentation: [2, { indentInsideParens: 'once-at-root-twice-in-block' }], + // Mimic 1tbs `} else {` brace style, like our JS + 'block-opening-brace-space-before': 'always', + 'block-closing-brace-newline-before': 'always-multi-line', + // Ensure multiple selectors on one line each + 'selector-list-comma-newline-before': 'never-multi-line', + 'selector-list-comma-newline-after': 'always', + // Trim unnecessary newlines/whitespace + 'block-closing-brace-empty-line-before': 'never', + 'max-empty-lines': 1, + 'no-eol-whitespace': true, + // Enforce spacing around various syntax symbols (colons, operators, etc.) + 'declaration-colon-space-after': 'always-single-line', + 'declaration-colon-space-before': 'never', + 'function-calc-no-unspaced-operator': true, + 'selector-combinator-space-before': 'always', + 'selector-combinator-space-after': 'always', + // Ensure trailing semicolons are always present on non-oneliners + 'declaration-block-semicolon-newline-after': 'always-multi-line', + + // Disable various opinionated extended stylelint rules that EUI has not previously enforced + 'scss/no-global-function-names': null, + 'scss/dollar-variable-empty-line-before': null, + 'scss/at-rule-conditional-no-parentheses': null, + 'scss/double-slash-comment-empty-line-before': null, + 'scss/at-if-no-null': null, + 'selector-not-notation': null, // Enforce comma notation for CSS-in-JS moving forward + }, + }, + { + files: ['**/*.styles.ts', '**/*.ts', '**/*.tsx'], + extends: ['stylelint-config-standard'], + customSyntax: 'postcss-styled-syntax', + rules: { + // Unfortunately, double slash comments must be replaced with standard CSS /* */ comments + // Otherwise we get a parsing error - see https://github.com/hudochenkov/postcss-styled-syntax#known-issues + 'no-invalid-double-slash-comments': true, + // Empty style keys should be allowed, as Emotion still uses them for generating classNames + 'no-empty-source': null, + // Don't lint casing on interpolated JS vars + 'function-name-case': ['lower', { ignoreFunctions: [cssInJsVarRegex] }], + 'function-no-unknown': [true, { ignoreFunctions: [cssInJsVarRegex] }], + 'value-keyword-case': [ + 'lower', + { + ignoreProperties: ['font-family'], + ignoreKeywords: [camelCaseValueRegex], + }, + ], + // This is set to deprecate after stylelint v16, but in the meanwhile, is helpful + // for finding extraneous semicolons after utils that already output semicolons (e.g. logicalCSS()) + 'no-extra-semicolons': true, + + // Emotion uses the `label` property to generate the output className string + 'property-no-unknown': [true, { ignoreProperties: 'label' }], + }, + }, + ], +}; diff --git a/packages/eui-theme-common/LICENSE.txt b/packages/eui-theme-common/LICENSE.txt new file mode 100644 index 00000000000..74327a8f6f3 --- /dev/null +++ b/packages/eui-theme-common/LICENSE.txt @@ -0,0 +1,6 @@ +Source code in this repository is covered by (i) a dual license under the Server +Side Public License, v 1 and the Elastic License 2.0 or (ii) an Apache License +2.0 compatible license or (iii) solely under the Elastic License 2.0, in each +case, as noted in the applicable header. The default throughout the repository +is a dual license under the Server Side Public License, v 1 and the Elastic +License 2.0, unless the header specifies another license. diff --git a/packages/eui-theme-common/README.md b/packages/eui-theme-common/README.md new file mode 100644 index 00000000000..294446d4b30 --- /dev/null +++ b/packages/eui-theme-common/README.md @@ -0,0 +1,11 @@ +# EUI common theming functionality and styling + +This package contains common code related to theming and styling. + +## Installing dependencies + +Please run `yarn` to install dependencies: + +```shell +yarn +``` \ No newline at end of file diff --git a/packages/eui-theme-common/babel.config.js b/packages/eui-theme-common/babel.config.js new file mode 100644 index 00000000000..8165fe45577 --- /dev/null +++ b/packages/eui-theme-common/babel.config.js @@ -0,0 +1,6 @@ +module.exports = { + presets: [ + ['@babel/preset-env', { targets: { node: 'current' } }], + '@babel/preset-typescript', + ], +}; diff --git a/packages/eui-theme-common/jest.config.js b/packages/eui-theme-common/jest.config.js new file mode 100644 index 00000000000..1b5b81ebf06 --- /dev/null +++ b/packages/eui-theme-common/jest.config.js @@ -0,0 +1,7 @@ +const config = { + moduleFileExtensions: ['ts', 'tsx', 'js', 'json'], + testEnvironment: 'node', + testMatch: ['**/*.test.js', '**/*.test.ts'], +}; + +module.exports = config; diff --git a/packages/eui-theme-common/package.json b/packages/eui-theme-common/package.json new file mode 100644 index 00000000000..6c255e6a605 --- /dev/null +++ b/packages/eui-theme-common/package.json @@ -0,0 +1,68 @@ +{ + "name": "@elastic/eui-theme-common", + "version": "0.0.1", + "description": "EUI theme common", + "license": "SEE LICENSE IN LICENSE.txt", + "scripts": { + "build:clean": "rimraf lib/", + "build": "yarn build:clean && yarn build:compile && yarn build:compile:cjs && yarn build:types", + "build:compile": "tsc --project ./tsconfig.json", + "build:compile:cjs": "NODE_ENV=production babel src --out-dir=lib/cjs --extensions .js,.ts,.tsx --source-maps", + "build:types": "NODE_ENV=production tsc --project tsconfig.types.json", + "build-pack": "yarn build && npm pack", + "lint": "yarn tsc --noEmit && yarn lint-es && yarn lint-sass", + "lint-es": "eslint --cache src/**/*.ts --max-warnings 0", + "lint-sass": "yarn stylelint \"**/*.scss\" --quiet-deprecation-warnings", + "test": "jest", + "pre-push": "yarn lint && yarn test" + }, + "repository": { + "type": "git", + "url": "https://github.com/elastic/eui.git", + "directory": "packages/eui-theme-common" + }, + "private": true, + "devDependencies": { + "@babel/cli": "^7.21.5", + "@babel/core": "^7.21.8", + "@babel/preset-env": "^7.21.5", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.21.5", + "@types/jest": "^29.5.12", + "@types/prettier": "2.7.3", + "@typescript-eslint/eslint-plugin": "^5.59.7", + "@typescript-eslint/parser": "^5.59.7", + "eslint": "^8.41.0", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-jest": "^28.5.0", + "eslint-plugin-local": "^1.0.0", + "eslint-plugin-prettier": "^4.2.1", + "jest": "^29.7.0", + "prettier": "^2.8.8", + "rimraf": "^6.0.1", + "stylelint": "^15.7.0", + "stylelint-config-prettier-scss": "^1.0.0", + "stylelint-config-standard": "^33.0.0", + "stylelint-config-standard-scss": "^9.0.0", + "typescript": "4.5.3" + }, + "main": "lib/cjs/index.js", + "exports": { + "./lib/*": "./lib/*", + "./scripts/*": "./scripts/*", + ".": { + "require": "./lib/cjs/index.js", + "import": "./lib/esm/index.js", + "default": "./lib/cjs/index.js" + } + }, + "files": [ + "lib/", + "src/**/*.scss", + "README.md" + ], + "installConfig": { + "hoistingLimits": "workspaces" + } +} diff --git a/packages/eui-theme-common/scripts/eslint-plugin-local/require_license_header.js b/packages/eui-theme-common/scripts/eslint-plugin-local/require_license_header.js new file mode 100644 index 00000000000..23b83648dd1 --- /dev/null +++ b/packages/eui-theme-common/scripts/eslint-plugin-local/require_license_header.js @@ -0,0 +1,132 @@ +const eslintParser = require('@typescript-eslint/parser'); + +function assert(truth, message) { + if (truth) { + return; + } + + const error = new Error(message); + error.failedAssertion = true; + throw error; +} + +function normalizeWhitespace(string) { + return string.replace(/\s+/g, ' '); +} + +function init(context, program, initStep) { + try { + return initStep(); + } catch (error) { + if (error.failedAssertion) { + context.report({ + node: program, + message: error.message, + }); + } else { + throw error; + } + } +} + +function isHashbang(text) { + return text.trim().startsWith('#!') && !text.trim().includes('\n'); +} + +module.exports = { + meta: { + fixable: 'code', + schema: [ + { + type: 'object', + properties: { + license: { + type: 'string', + }, + }, + additionalProperties: false, + }, + ], + }, + create: (context) => { + return { + Program(program) { + const license = init(context, program, function () { + const options = context.options[0] || {}; + const license = options.license; + + assert(!!license, '"license" option is required'); + + const parsed = eslintParser.parse(license, { comment: true }); + assert( + !parsed.body.length, + '"license" option must only include a single comment' + ); + assert( + parsed.comments.length === 1, + '"license" option must only include a single comment' + ); + + return { + source: license, + nodeValue: normalizeWhitespace(parsed.comments[0].value), + }; + }); + + if (!license) { + return; + } + + const sourceCode = context.getSourceCode(); + const comment = sourceCode + .getAllComments() + .find( + (node) => normalizeWhitespace(node.value) === license.nodeValue + ); + + // no licence comment + if (!comment) { + context.report({ + message: 'File must start with a license header', + loc: { + start: { line: 1, column: 0 }, + end: { line: 1, column: sourceCode.lines[0].length - 1 }, + }, + fix(fixer) { + if (isHashbang(sourceCode.lines[0])) { + return undefined; + } + + return fixer.replaceTextRange([0, 0], license.source + '\n\n'); + }, + }); + return; + } + + // ensure there is nothing before the comment + const sourceBeforeNode = sourceCode + .getText() + .slice(0, sourceCode.getIndexFromLoc(comment.loc.start)); + if (sourceBeforeNode.length && !isHashbang(sourceBeforeNode)) { + context.report({ + node: comment, + message: 'License header must be at the very beginning of the file', + fix(fixer) { + // replace leading whitespace if possible + if (sourceBeforeNode.trim() === '') { + return fixer.replaceTextRange([0, sourceBeforeNode.length], ''); + } + + // inject content at top and remove node from current location + // if removing whitespace is not possible + return [ + fixer.remove(comment), + fixer.replaceTextRange([0, 0], license.source + '\n\n'), + ]; + }, + }); + } + }, + }; + }, +}; diff --git a/packages/eui-theme-common/scripts/eslint-plugin-local/require_license_header.test.js b/packages/eui-theme-common/scripts/eslint-plugin-local/require_license_header.test.js new file mode 100644 index 00000000000..904dff17bc0 --- /dev/null +++ b/packages/eui-theme-common/scripts/eslint-plugin-local/require_license_header.test.js @@ -0,0 +1,177 @@ +const { RuleTester } = require('eslint'); +const rule = require('./require_license_header'); +const dedent = require('dedent'); + +const ruleTester = new RuleTester({ + parser: require.resolve('@typescript-eslint/parser'), +}); + +ruleTester.run('@kbn/eslint/require-license-header', rule, { + valid: [ + { + code: dedent` + /* license */ + + console.log('foo') + `, + + options: [{ license: '/* license */' }], + }, + { + code: dedent` + // license + + console.log('foo') + `, + + options: [{ license: '// license' }], + }, + ], + + invalid: [ + // missing license option + { + code: dedent` + console.log('foo') + `, + + options: [], + errors: [ + { + message: '"license" option is required', + }, + ], + }, + + // content cannot contain multiple block comments + { + code: dedent` + console.log('foo') + `, + + options: [{ license: '/* one *//* two */' }], + errors: [ + { + message: '"license" option must only include a single comment', + }, + ], + }, + + // content cannot contain multiple line comments + { + code: dedent` + console.log('foo') + `, + + options: [{ license: `// one\n// two` }], + errors: [ + { + message: '"license" option must only include a single comment', + }, + ], + }, + + // content cannot contain expressions + { + code: dedent` + console.log('foo') + `, + + options: [ + { + license: dedent` + /* license */ + console.log('hello world'); + `, + }, + ], + errors: [ + { + message: '"license" option must only include a single comment', + }, + ], + }, + + // content is not a single comment + { + code: dedent` + console.log('foo') + `, + + options: [{ license: `console.log('hello world');` }], + errors: [ + { + message: '"license" option must only include a single comment', + }, + ], + }, + + // missing license header + { + code: dedent` + console.log('foo') + `, + + options: [{ license: '/* license */' }], + errors: [ + { + message: 'File must start with a license header', + }, + ], + + output: dedent` + /* license */ + + console.log('foo') + `, + }, + + // strips newlines before the license comment + { + code: + '\n\n' + + dedent` + /* license */ + + console.log('foo') + `, + + options: [{ license: '/* license */' }], + errors: [ + { + message: 'License header must be at the very beginning of the file', + }, + ], + + output: dedent` + /* license */ + + console.log('foo') + `, + }, + + // moves license header before other nodes if necessary + { + code: dedent` + /* not license */ + /* license */ + console.log('foo') + `, + + options: [{ license: '/* license */' }], + errors: [ + { + message: 'License header must be at the very beginning of the file', + }, + ], + + output: dedent` + /* license */ + + /* not license */ + + console.log('foo') + `, + }, + ], +}); diff --git a/packages/eui-theme-common/src/global_styling/functions/_colors.scss b/packages/eui-theme-common/src/global_styling/functions/_colors.scss new file mode 100644 index 00000000000..25834e10add --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/functions/_colors.scss @@ -0,0 +1,138 @@ +// Converting a normal hex color to RBG +@function hexToRGB($color) { + @return 'rgb%28#{round(red($color))}, #{round(green($color))}, #{round(blue($color))}%29'; +} + +// Mixes a provided color with white. +@function tint($color, $percent) { + @return mix($euiColorGhost, $color, $percent); +} + +// Mixes a provided color with black. +@function shade($color, $percent) { + @return mix($euiColorInk, $color, $percent); +} + +// For theming. Checks the text color and tells us whether it's light or dark. +// Based on that we either tint (add white) or shade (add black). +@function tintOrShade($color, $tint, $shade) { + @if (lightness($euiTextColor) > 50) { + @return shade($color, $shade); + } @else { + @return tint($color, $tint); + } +} + +// The reverse of the above +@function shadeOrTint($color, $shade, $tint) { + @if (lightness($euiTextColor) < 50) { + @return shade($color, $shade); + } @else { + @return tint($color, $tint); + } +} + +// Similar to above, but uses the light or dark color based +// on whether it's the light or dark theme +@function lightOrDarkTheme($lightColor, $darkColor) { + @if (lightness($euiTextColor) < 50) { + @return $lightColor; + } @else { + @return $darkColor; + } +} + +// Calculates luminance, which is better than brightness for checking colors +// pow, nth functions come from the _math.scss functions +@function luminance($color) { + // Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + $rgba: red($color), green($color), blue($color); + $rgba2: (); + + @for $i from 1 through 3 { + $rgb: nth($rgba, $i); + $rgb: $rgb / 255; + + $rgb: if($rgb < .03928, $rgb / 12.92, pow(($rgb + .055) / 1.055, 2.4)); + + $rgba2: append($rgba2, $rgb); + } + + @return .2126 * nth($rgba2, 1) + .7152 * nth($rgba2, 2) + .0722 * nth($rgba2, 3); +} + +// Calculate contrast +@function contrastRatio($background, $foreground) { + $backgroundLum: luminance($background) + .05; + $foregroundLum: luminance($foreground) + .05; + + @return max($backgroundLum, $foregroundLum) / min($backgroundLum, $foregroundLum); +} + +// Given $color, decide whether $lightText or $darkText should be used as the text color +// ex: chooseLightOrDarkText(#EEE, #FFF, #000) would return #000 because it has +// a higher contrast than #FFF against a #EEE background. +@function chooseLightOrDarkText($background, $lightText: $euiColorGhost, $darkText: $euiColorInk) { + $lightContrast: contrastRatio($background, $lightText); + $darkContrast: contrastRatio($background, $darkText); + + @if ($lightContrast > $darkContrast) { + @return $lightText; + } @else { + @return $darkText; + } +} + +// Given a $foreground and a $background, make the $foreground AA accessibility by slightly +// adjusting it till the contrast is high enough +// By default it will compare against the page background color + +// ex: makeContrastColor($lightPink, #FFF) would continually shade the pink until +// it had higher than 4.5 contrast on a white background. +$euiContrastRatioText: 4.5; +@function makeHighContrastColor($foreground, $background: $euiPageBackgroundColor, $ratio: $euiContrastRatioText) { + $contrast: contrastRatio($foreground, $background); + + // Determine the lightness factor of the background color first to + // determine whether to shade or tint the foreground. + $brightness: lightness($background); + + $highContrastTextColor: $foreground; + + @while ($contrast < $ratio) { + @if ($brightness > 50) { + $highContrastTextColor: shade($highContrastTextColor, 5%); + } @else { + $highContrastTextColor: tint($highContrastTextColor, 5%); + } + + $contrast: contrastRatio($highContrastTextColor, $background); + + @if (lightness($highContrastTextColor) < 5) { + @warn 'High enough contrast could not be determined. Most likely your background color does not adjust for light mode.'; + @return $highContrastTextColor; + } + + @if (lightness($highContrastTextColor) > 95) { + @warn 'High enough contrast could not be determined. Most likely your background color does not adjust for dark mode.'; + @return $highContrastTextColor; + } + } + + @return $highContrastTextColor; +} + +// Graphics such as stand alone icons and pieces of a graph only need a minimum ratio of 3:1 with its background. +// Therefore, we can reuse the `makeHighContrastColor()` function but only attain a min contrast of 3.0. +// It is still recommended to use `makeHighContrastColor()` to attain a 4.5:1 ratio if the graphic is small or thinly stroked. +// https://www.w3.org/WAI/GL/low-vision-a11y-tf/wiki/Informational_Graphic_Contrast_(Minimum) +$euiContrastRatioGraphic: 3; +@function makeGraphicContrastColor($color, $background: $euiPageBackgroundColor) { + @return makeHighContrastColor($color, $background, $euiContrastRatioGraphic); +} + +// Disabled content only needs a contrast of at least 2 because there is no interaction available +$euiContrastRatioDisabled: 2; +@function makeDisabledContrastColor($color, $background: $euiPageBackgroundColor) { + @return makeHighContrastColor($color, $background, $euiContrastRatioDisabled); +} diff --git a/packages/eui-theme-common/src/global_styling/functions/_index.scss b/packages/eui-theme-common/src/global_styling/functions/_index.scss new file mode 100644 index 00000000000..de8260b2bba --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/functions/_index.scss @@ -0,0 +1,5 @@ +// Math needs to be first in the load order +@import 'math'; + +// Using math, we have functions to manipulate contrast / luminosity for accessibility +@import 'colors'; diff --git a/packages/eui-theme-common/src/global_styling/functions/_math.scss b/packages/eui-theme-common/src/global_styling/functions/_math.scss new file mode 100644 index 00000000000..cdec36f3e60 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/functions/_math.scss @@ -0,0 +1 @@ +@import 'math_pow'; \ No newline at end of file diff --git a/packages/eui-theme-common/src/global_styling/functions/_math_pow.scss b/packages/eui-theme-common/src/global_styling/functions/_math_pow.scss new file mode 100644 index 00000000000..2e2d784a845 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/functions/_math_pow.scss @@ -0,0 +1,82 @@ +/** +The MIT License (MIT) + +Copyright (c) 2015 strarsis https://github.com/strarsis/sass-math-pow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +@function pow($number, $exp) { + $exp1: round($exp); + $result: powInt($number, $exp1); + + @if ($exp1 != $exp) { + $result: $result * mathExp(($exp - $exp1) * mathLn($number)); + } + + @return $result; +} + +@function powInt($number, $exp) { + @if $exp == 0 { + @return 1; + } @else if $exp < 0 { + @return 1 / powInt($number, -$exp); + } @else { + $e: floor($exp / 2); + $pow: pow($number, $e); + @if $e * 2 == $exp { + @return $pow * $pow; + } @else { + @return $pow * $pow * $number; + } + } +} + +@function mathExp($value) { + $item: 1; + $result: 1; + + @for $index from 1 to 100 { + $item: $item * $value / $index; + $result: $result + $item; + } + + @return $result; +} + +@function mathLn($value) { + $tenExp: 0; + $lnTen: 2.30258509; + + @while ($value > 1) { + $tenExp: $tenExp + 1; + $value: $value / 10; + } + + $item: -1; + $result: 0; + + @for $index from 1 to 100 { + $item: $item * (1 - $value); + $result: $result + $item / $index; + } + + @return $result + $tenExp * $lnTen; +} diff --git a/packages/eui-theme-common/src/global_styling/functions/index.ts b/packages/eui-theme-common/src/global_styling/functions/index.ts new file mode 100644 index 00000000000..5b07f8b8473 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/functions/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './size'; diff --git a/packages/eui-theme-common/src/global_styling/functions/size.ts b/packages/eui-theme-common/src/global_styling/functions/size.ts new file mode 100644 index 00000000000..a96b5b85452 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/functions/size.ts @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* TODO: move to a shared module package */ + +/** + * Calculates the `px` value based on a scale multiplier + * @param scale - The font scale multiplier + * * + * @param themeOrBase - Theme base value + * * + * @returns string - Rem unit aligned to baseline + */ + +export const sizeToPixel = + (scale: number = 1) => + (themeOrBase: number | { base: number; [key: string]: any }) => { + const base = + typeof themeOrBase === 'object' ? themeOrBase.base : themeOrBase; + return `${base * scale}px`; + }; diff --git a/packages/eui-theme-common/src/global_styling/index.scss b/packages/eui-theme-common/src/global_styling/index.scss new file mode 100644 index 00000000000..5c29e0435a5 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/index.scss @@ -0,0 +1,18 @@ +// Core + +// Functions need to be first, since we use them in our variables and mixin definitions +@import 'functions/index'; + +// Variables come next, and are used in some mixins +@import 'variables/index'; + +// Mixins provide generic code expansion through helpers +@import 'mixins/index'; + +// Utility classes provide one-off selectors for common css problems +@import 'utility/index'; + +// The reset file has moved to global_styles.tsx + +// Customization of the React Date Picker +@import 'react_date_picker/index'; diff --git a/packages/eui-theme-common/src/global_styling/index.ts b/packages/eui-theme-common/src/global_styling/index.ts new file mode 100644 index 00000000000..47fa2bee007 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './types'; +export * from './functions'; +export * from './variables'; diff --git a/packages/eui-theme-common/src/global_styling/mixins/_button.scss b/packages/eui-theme-common/src/global_styling/mixins/_button.scss new file mode 100644 index 00000000000..47c87b6e742 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_button.scss @@ -0,0 +1,149 @@ +// Provides a solid reset and base for handling sizing layout +// Does not include any visual styles +@mixin euiButtonBase { + display: inline-block; + appearance: none; + cursor: pointer; + height: $euiButtonHeight; + line-height: $euiButtonHeight; // prevents descenders from getting cut off + text-align: center; + white-space: nowrap; + max-width: 100%; + vertical-align: middle; +} + +// Adds the focus (and hover) animation for translating up 1px +@mixin euiButtonFocus { + @include euiCanAnimate { + transition: transform $euiAnimSpeedNormal ease-in-out, background-color $euiAnimSpeedNormal ease-in-out; + + &:hover:not(:disabled) { + transform: translateY(-1px); + } + + &:focus { + animation: euiButtonActive $euiAnimSpeedNormal $euiAnimSlightBounce; + } + + &:active:not(:disabled) { + transform: translateY(1px); + } + } +} + +// All of the button base styles including the base, focus, font, and initial styles +// Does not include individual alterations like color or sizes +@mixin euiButton { + @include euiButtonBase; + @include euiFont; + @include euiFontSize; + @include euiButtonFocus; + + font-weight: $euiButtonFontWeight; + text-decoration: none; + outline-offset: -1px; + + &:hover:not(:disabled), + &:focus { + text-decoration: underline; + } +} + +// Correctly lays out the contents of a button when using the proper dom elements of: +// +// 1. Apply margin to all but last item in the flex. +// 2. Margin gets flipped because of the row-reverse. +@mixin euiButtonContent($isReverse: false) { + height: 100%; + width: 100%; + vertical-align: middle; + + .euiButtonContent__icon, + .euiButtonContent__spinner { + flex-shrink: 0; // Ensures the icons/spinner don't scale down below their intended size + } + + @if ($isReverse) { + flex-direction: row-reverse; + + > * + * { + margin-inline-start: 0; // 1, 2 + margin-inline-end: $euiSizeS; // 1, 2 + } + } @else { + display: flex; + justify-content: center; + align-items: center; + + > * + * { + margin-inline-start: $euiSizeS; // 1 + } + } +} + +@mixin euiButtonContentDisabled { + pointer-events: auto; + cursor: not-allowed; + + &:hover, + &:focus, + &:focus-within { + text-decoration: none; + } + + .euiButtonContent__spinner { + border-color: euiLoadingSpinnerBorderColors(currentColor); + } +} + +/* + * Creates the Amsterdam style of button with a transparent background + */ +@mixin euiButtonDefaultStyle($color: 'primary', $includeStates: true, $transparency: $euiButtonDefaultTransparency) { + $backgroundColor: $color; + + @if (map-has-key($euiButtonTypes, $color)) { + $backgroundColor: map-get($euiButtonTypes, $color); + } + + $percentConversion: $transparency * 100%; + // This variable simulates the possibly darkest background the button could be on + // Simulates the 20% opaque color on top of the page background color + $backgroundColorSimulated: mix($euiPageBackgroundColor, $backgroundColor, $percentConversion); + // Then we can calculate the darkest text color needed + color: makeHighContrastColor($backgroundColor, $backgroundColorSimulated); + // But still use transparency + background-color: transparentize($backgroundColor, $transparency); + + @if ($includeStates) { + &:not([class*='isDisabled']) { + &:hover, + &:focus { + // Duplicated from inert state simply to override default theme + background-color: transparentize($backgroundColor, $transparency); + } + } + } +} + +/* + * Creates the Amsterdam style of fill button + */ +@mixin euiButtonFillStyle($color: 'primary') { + $backgroundColor: $color; + + @if (map-has-key($euiButtonTypes, $color)) { + $backgroundColor: map-get($euiButtonTypes, $color); + } + + background-color: $backgroundColor; + color: chooseLightOrDarkText($backgroundColor); +} + +// Keyframe animation declarations can be found in +// utility/animations.scss diff --git a/packages/eui-theme-common/src/global_styling/mixins/_form.scss b/packages/eui-theme-common/src/global_styling/mixins/_form.scss new file mode 100644 index 00000000000..326a8491f87 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_form.scss @@ -0,0 +1,273 @@ +@mixin euiFormControlLayoutPadding($numOfIcons, $side: 'right', $compressed: false) { + $iconSize: $euiSize; + $iconPadding: $euiFormControlPadding; + $marginBetweenIcons: $euiFormControlPadding / 2; + + @if ($compressed) { + $iconPadding: $euiFormControlCompressedPadding; + } + + @if variable-exists(numOfIcons) == false { + @error '$numOfIcons:integer (1-3) must be provided to @mixin euiFormControlLayoutPadding().'; + } @else if $numOfIcons == 1 { + padding-#{$side}: $iconPadding + $iconSize + $iconPadding; + } @else if $numOfIcons == 2 { + padding-#{$side}: $iconPadding + $iconSize + $marginBetweenIcons + $iconSize + $iconPadding; + } @else if $numOfIcons == 3 { + padding-#{$side}: $iconPadding + $iconSize + $marginBetweenIcons + $iconSize + $marginBetweenIcons + $iconSize + $iconPadding; + } +} + +@mixin euiPlaceholderPerBrowser { + // stylelint-disable selector-no-vendor-prefix + // Each prefix must be its own content block + &::-webkit-input-placeholder { @content; opacity: 1; } + &::-moz-placeholder { @content; opacity: 1; } + &:-ms-input-placeholder { @content; opacity: 1; } + &:-moz-placeholder { @content; opacity: 1; } + &::placeholder { @content; opacity: 1; } +} + +@function euiFormControlGradient($color: $euiColorPrimary) { + @return linear-gradient(to top, + $color, + $color 2px, + transparent 2px, + transparent 100% + ); +} + +@mixin euiFormControlText { + @include euiFont; + font-size: $euiFontSizeS; + color: $euiTextColor; + + @include euiPlaceholderPerBrowser { + color: $euiFormControlPlaceholderText; + } +} + +@mixin euiFormControlSize( + $height: $euiFormControlHeight, + $includeAlternates: false +) { + // Default + max-width: $euiFormMaxWidth; + width: 100%; + height: $height; + + @if ($includeAlternates) { + &--fullWidth { + max-width: 100%; + } + + &--compressed { + height: $euiFormControlCompressedHeight; + } + + &--inGroup { + height: 100%; + } + } +} + +@mixin euiFormControlWithIcon($isIconOptional: false, $side: 'left', $compressed: false) { + @if ($isIconOptional) { + @at-root { + #{&}--withIcon { + @include euiFormControlLayoutPadding(1, $side, $compressed); + } + } + } @else { + @include euiFormControlLayoutPadding(1, $side, $compressed); + } +} + +@mixin euiFormControlIsLoading($isNextToIcon: false) { + @at-root { + #{&}-isLoading { + @if ($isNextToIcon) { + @include euiFormControlLayoutPadding(2); + } @else { + @include euiFormControlLayoutPadding(1); + } + } + + #{&}-isLoading#{&}--compressed { + @if ($isNextToIcon) { + @include euiFormControlLayoutPadding(2, $compressed: true); + } @else { + @include euiFormControlLayoutPadding(1, $compressed: true); + } + } + } +} + +// 1. Must supply both values to background-size or some browsers apply the single value to both directions + +@mixin euiFormControlDefaultShadow($borderOnly: false) { + background-color: $euiFormBackgroundColor; + background-repeat: no-repeat; + background-size: 0% 100%; // 1 + + @if ($borderOnly) { + box-shadow: inset 0 0 0 1px $euiFormBorderColor; + } @else { + box-shadow: + #{$euiFormControlBoxShadow}, + inset 0 0 0 1px $euiFormBorderColor; + } + + transition: + box-shadow $euiAnimSpeedFast ease-in, + background-image $euiAnimSpeedFast ease-in, + background-size $euiAnimSpeedFast ease-in, + background-color $euiAnimSpeedFast ease-in; + + // Fixes bug in Firefox where adding a transition to the background-color + // caused a flash of differently styled dropdown. + @supports (-moz-appearance: none) { + // List *must* be in the same order as the above. + transition-property: box-shadow, background-image, background-size; + } +} + +@mixin euiFormControlFocusStyle($borderOnly: false) { + background-color: tintOrShade($euiColorEmptyShade, 0%, 40%); + background-image: euiFormControlGradient(); + background-size: 100% 100%; // 1 + outline: none; // Blanket remove all outlines relying on our own bottom border + + @if ($borderOnly) { + box-shadow: inset 0 0 0 1px $euiFormBorderColor; + } @else { + box-shadow: inset 0 0 0 1px $euiFormBorderColor; + } +} + +@mixin euiFormControlInvalidStyle { + background-image: euiFormControlGradient($euiColorDanger); + background-size: 100%; +} + +@mixin euiFormControlDisabledTextStyle { + color: $euiFormControlDisabledColor; + -webkit-text-fill-color: $euiFormControlDisabledColor; // Required for Safari +} + +@mixin euiFormControlDisabledStyle { + @include euiFormControlDisabledTextStyle; + cursor: not-allowed; + background: $euiFormBackgroundDisabledColor; + box-shadow: inset 0 0 0 1px $euiFormBorderDisabledColor; + + @include euiPlaceholderPerBrowser { + color: $euiFormControlDisabledColor; + } +} + +@mixin euiFormControlReadOnlyStyle { + cursor: default; + color: $euiTextColor; + -webkit-text-fill-color: $euiTextColor; // Required for Safari + // Use transparency since there is no border and in case form is on a non-white background + background: $euiFormBackgroundReadOnlyColor; + border-color: transparent; + box-shadow: inset 0 0 0 1px $euiFormBorderDisabledColor; +} + +// 2. Override invalid state with focus state. + +@mixin euiFormControlStyle($borderOnly: false, $includeStates: true, $includeSizes: true) { + @include euiFormControlSize($includeAlternates: $includeSizes); + @include euiFormControlDefaultShadow; + @include euiFormControlText; + + border: none; + border-radius: $euiFormControlBorderRadius; + padding: $euiFormControlPadding; + + @if ($includeStates) { + &:invalid { // 2 + @include euiFormControlInvalidStyle; + } + + &:focus { // 2 + @include euiFormControlFocusStyle; + } + + &:disabled { + @include euiFormControlDisabledStyle; + } + + &[readOnly] { + @include euiFormControlReadOnlyStyle; + } + + // Needs to be set for autofill + &:-webkit-autofill { + -webkit-text-fill-color: lightOrDarkTheme($euiColorDarkestShade, $euiColorLightShade); + + ~ .euiFormControlLayoutIcons { + color: lightOrDarkTheme($euiColorDarkestShade, $euiColorLightShade); + } + } + } + + @if ($includeSizes) { + &--compressed { + @include euiFormControlStyleCompressed($borderOnly, $includeStates); + } + + &--inGroup { + // stylelint-disable-next-line declaration-no-important + box-shadow: none !important; + border-radius: 0; + } + } +} + +@mixin euiFormControlStyleCompressed($borderOnly: false, $includeStates: true) { + @include euiFormControlDefaultShadow($borderOnly: true); + padding: $euiFormControlCompressedPadding; + border-radius: $euiFormControlCompressedBorderRadius; + + @if ($includeStates) { + &:invalid { // 2 + @include euiFormControlInvalidStyle; + } + + &:focus { // 2 + @include euiFormControlFocusStyle($borderOnly: true); + } + + &:disabled { + @include euiFormControlDisabledStyle; + } + + &[readOnly] { + @include euiFormControlReadOnlyStyle; + } + } +} + +@mixin euiHiddenSelectableInput { + position: absolute; + // stylelint-disable-next-line declaration-no-important + opacity: 0 !important; // Make sure it's still hidden when :disabled + width: 100%; + height: 100%; + cursor: pointer; +} + +// Adjusts form controls border radius +@mixin euiFormControlSideBorderRadius($borderRadius, $side, $internal: false) { + @if $internal == true { + $borderRadius: $borderRadius - 1; + } + @if $side == 'left' { + border-radius: $borderRadius 0 0 $borderRadius; + } @else if $side == 'right' { + border-radius: 0 $borderRadius $borderRadius 0; + } +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_helpers.scss b/packages/eui-theme-common/src/global_styling/mixins/_helpers.scss new file mode 100644 index 00000000000..00688ef63ef --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_helpers.scss @@ -0,0 +1,126 @@ +// Helper mixins + +// Set scroll bar appearance on Chrome (and firefox). +@mixin euiScrollBar($thumbColor: $euiColorDarkShade, $trackBackgroundColor: transparent, $size: 'thin') { + // Firefox's scrollbar coloring cascades, but the sizing does not, + // so it's being added to this mixin for allowing support wherever custom scrollbars are + scrollbar-color: transparentize($thumbColor, .5) $trackBackgroundColor; // Firefox support + + @if ($size == 'thin') { + scrollbar-width: thin; + } + + // stylelint-disable selector-no-vendor-prefix + &::-webkit-scrollbar { + width: $euiScrollBar; + height: $euiScrollBar; + } + + &::-webkit-scrollbar-thumb { + background-color: transparentize($thumbColor, .5); + background-clip: content-box; + border-radius: $euiScrollBar; + + @if ($size == 'thin') { + border: $euiScrollBarCornerThin solid $trackBackgroundColor; + } @else { + border: $euiScrollBarCorner solid $trackBackgroundColor; + } + } + + &::-webkit-scrollbar-corner, + &::-webkit-scrollbar-track { + background-color: $trackBackgroundColor; + } +} + +/** + * 1. Focus rings shouldn't be visible on scrollable regions, but a11y requires them to be focusable. + * Browser's supporting `:focus-visible` will still show outline on keyboard focus only. + * Others like Safari, won't show anything at all. + * 2. Force the `:focus-visible` when the `tabindex=0` (is tabbable) + */ + +// Just overflow and scrollbars +@mixin euiYScroll { + @include euiScrollBar; + height: 100%; + overflow-y: auto; + overflow-x: hidden; + + &:focus { + outline: none; /* 1 */ + } + + &[tabindex='0']:focus:focus-visible { + outline-style: auto; /* 2 */ + } +} + +@mixin euiXScroll { + @include euiScrollBar; + overflow-x: auto; + + &:focus { + outline: none; /* 1 */ + } + + &[tabindex='0']:focus:focus-visible { + outline-style: auto; /* 2 */ + } +} + +// The full overflow with shadow +@mixin euiYScrollWithShadows { + @include euiYScroll; + @include euiOverflowShadow('y'); +} + +@mixin euiXScrollWithShadows { + @include euiXScroll; + @include euiOverflowShadow('x'); +} + +/** + * For quickly applying a full-height element whether using flex or not + */ +@mixin euiFullHeight { + height: 100%; + flex: 1 1 auto; + overflow: hidden; +} + +// Hiding elements offscreen to only be read by screen reader +// See https://github.com/elastic/eui/pull/5130 and https://github.com/elastic/eui/pull/5152 for more info +@mixin euiScreenReaderOnly { + // Take the element out of the layout + position: absolute; + // Keep it vertically inline + top: auto; + // Chrome requires a left value, and Selenium (used by Kibana's FTR) requires an off-screen position for its .getVisibleText() to not register SR-only text + left: -10000px; + // The element must have a size (for some screen readers) + width: 1px; + height: 1px; + // But reduce the visible size to nothing + clip: rect(0 0 0 0); + clip-path: inset(50%); + // And ensure no overflows occur + overflow: hidden; + // Chrome requires the negative margin to not cause overflows of parent containers + margin: -1px; +} + +// Doesn't have reduced motion turned on +@mixin euiCanAnimate { + @media screen and (prefers-reduced-motion: no-preference) { + @content; + } +} + +// Does have reduced motion turned on +@mixin euiCantAnimate { + @media screen and (prefers-reduced-motion: reduce) { + @content; + } +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_index.scss b/packages/eui-theme-common/src/global_styling/mixins/_index.scss new file mode 100644 index 00000000000..7d0cba8a92e --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_index.scss @@ -0,0 +1,14 @@ +@import 'responsive'; +@import 'shadow'; +@import 'size'; +@import 'typography'; +@import 'helpers'; +@import 'states'; + +@import 'button'; +@import 'form'; +@import 'loading'; +@import 'link'; +@import 'panel'; +@import 'range'; +@import 'tool_tip'; diff --git a/packages/eui-theme-common/src/global_styling/mixins/_link.scss b/packages/eui-theme-common/src/global_styling/mixins/_link.scss new file mode 100644 index 00000000000..98dac59b9cc --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_link.scss @@ -0,0 +1,11 @@ +@mixin euiLink { + text-align: left; + + &:hover { + text-decoration: underline; + } + + &:focus { + text-decoration: underline; + } +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_loading.scss b/packages/eui-theme-common/src/global_styling/mixins/_loading.scss new file mode 100644 index 00000000000..0f72a8433f7 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_loading.scss @@ -0,0 +1,6 @@ +@function euiLoadingSpinnerBorderColors( + $main: $euiColorLightShade, + $highlight: $euiColorPrimary +) { + @return $highlight $main $main $main; +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_panel.scss b/packages/eui-theme-common/src/global_styling/mixins/_panel.scss new file mode 100644 index 00000000000..4eb0a5fb55a --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_panel.scss @@ -0,0 +1,55 @@ +@mixin euiPanel($selector) { + @if variable-exists(selector) == false { + @error 'A $selector must be provided to @mixin euiPanel().'; + } @else { + #{$selector} { + flex-grow: 1; + + &#{$selector}--flexGrowZero { + flex-grow: 0; + } + + &#{$selector}--hasShadow { + @include euiBottomShadowMedium; + } + + &#{$selector}--hasBorder { + border: $euiBorderThin; + box-shadow: none; + } + + &#{$selector}--isClickable { + // transition the shadow + transition: all $euiAnimSpeedFast $euiAnimSlightResistance; + + &:enabled { // This is a good selector for buttons since it doesn't exist on divs + // in case of button wrapper which inherently is inline-block and no width + display: block; + width: 100%; + text-align: left; + } + + &:hover, + &:focus { + @include euiBottomShadow; + transform: translateY(-2px); + cursor: pointer; + } + } + + // Border Radii + @each $modifier, $amount in $euiPanelBorderRadiusModifiers { + &#{$selector}--#{$modifier} { + border-radius: $amount; + } + } + + // Background colors + @each $modifier, $amount in $euiPanelBackgroundColorModifiers { + &#{$selector}--#{$modifier} { + background-color: $amount; + } + } + } + } +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_range.scss b/packages/eui-theme-common/src/global_styling/mixins/_range.scss new file mode 100644 index 00000000000..ec47e39e2d6 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_range.scss @@ -0,0 +1,62 @@ +/* +The CSS in JS version of this file lives in: + - src/components/form/range/range.styles.ts + +The following files still use the Sass version: + - src/themes/amsterdam/overrides/_color_stops.scss + - src/themes/amsterdam/overrides/_hue.scss +*/ + +@mixin euiRangeTrackSize($compressed: false) { + height: $euiRangeTrackHeight; + width: $euiRangeTrackWidth; + + @if ($compressed) { + height: $euiRangeTrackCompressedHeight; + } +} + +@mixin euiRangeTrackPerBrowser { + &::-webkit-slider-runnable-track { @content; } + &::-moz-range-track { @content; } + &::-ms-fill-lower { @content; } + &::-ms-fill-upper { @content; } +} + +@mixin euiRangeThumbBorder { + border: 2px solid $euiRangeThumbBorderColor; +} + +@mixin euiRangeThumbBoxShadow { + box-shadow: + 0 0 0 1px $euiRangeThumbBorderColor, + 0 2px 2px -1px rgba($euiShadowColor, .2), + 0 1px 5px -2px rgba($euiShadowColor, .2); +} + +@mixin euiRangeThumbFocusBoxShadow { + box-shadow: 0 0 0 2px $euiFocusRingColor; +} + +@mixin euiRangeThumbStyle { + @include euiRangeThumbBoxShadow; + @include euiRangeThumbBorder; + cursor: pointer; + background-color: $euiRangeThumbBackgroundColor; + padding: 0; + height: $euiRangeThumbHeight; + width: $euiRangeThumbWidth; + box-sizing: border-box; // required for firefox or the border makes the width and height to increase +} + +@mixin euiRangeThumbPerBrowser { + &::-webkit-slider-thumb { @content; } + &::-moz-range-thumb { @content; } + &::-ms-thumb { @content; } +} + +@mixin euiRangeThumbFocus { + @include euiRangeThumbBorder; + @include euiRangeThumbFocusBoxShadow; + background-color: $euiColorPrimary; +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_responsive.scss b/packages/eui-theme-common/src/global_styling/mixins/_responsive.scss new file mode 100644 index 00000000000..0fa3a9b08a8 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_responsive.scss @@ -0,0 +1,44 @@ +// A sem-complicated mixin for breakpoints, that takes any number of +// named breakpoints that exists in $euiBreakpoints. + +@mixin euiBreakpoint($sizes...) { + // Loop through each size parameter + @each $size in $sizes { + // Store the location of the size in the list to check against + $index: index($euiBreakpointKeys, $size); + + // Check to make sure it exists in the allowed breakpoint names + @if ( $index ) { + + // Set the min size to the value of the size + $minSize: map-get($euiBreakpoints, $size); + + // If it is the last item, don't give it a max-width + @if ( $index == length($euiBreakpointKeys) ) { + @media only screen and (min-width: $minSize) { + @content; + } + // If it's not the last item, add a max-width + } @else { + + // Set the max size to the value of the next size (-1px as to not overlap) + $maxSize: map-get($euiBreakpoints, nth($euiBreakpointKeys, $index + 1)) - 1px; + + // If it's the the first item, don't set a min-width + @if ( $index == 1 ) { + @media only screen and (max-width: $maxSize) { + @content; + } + // Otherwise it should have a min and max width + } @else { + @media only screen and (min-width: $minSize) and (max-width: $maxSize) { + @content; + } + } + } + // If it's not a known breakpoint, throw a warning + } @else { + @warn "euiBreakpoint(): '#{$size}' is not a valid size in $euiBreakpoints. Accepted values are '#{$euiBreakpointKeys}'"; + } + } +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_shadow.scss b/packages/eui-theme-common/src/global_styling/mixins/_shadow.scss new file mode 100644 index 00000000000..1bc64eb085b --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_shadow.scss @@ -0,0 +1,108 @@ +@function shadowOpacity($opacity) { + @if (lightness($euiTextColor) < 50) { + @return $opacity * 1; + } @else { + @return $opacity * 2.5; + } +} + +@mixin euiSlightShadow($color: $euiShadowColor) { + box-shadow: + 0 .8px .8px rgba($color, shadowOpacity(.04)), + 0 2.3px 2px rgba($color, shadowOpacity(.03)); +} + +@mixin euiBottomShadowSmall($color: $euiShadowColor) { + box-shadow: + 0 .7px 1.4px rgba($color, shadowOpacity(.07)), + 0 1.9px 4px rgba($color, shadowOpacity(.05)), + 0 4.5px 10px rgba($color, shadowOpacity(.05)); +} + +@mixin euiBottomShadowMedium($color: $euiShadowColor) { + box-shadow: + 0 .9px 4px -1px rgba($color, shadowOpacity(.08)), + 0 2.6px 8px -1px rgba($color, shadowOpacity(.06)), + 0 5.7px 12px -1px rgba($color, shadowOpacity(.05)), + 0 15px 15px -1px rgba($color, shadowOpacity(.04)); +} + +// Similar to shadow medium but without the bottom depth. Useful for popovers +// that drop UP rather than DOWN. +@mixin euiBottomShadowFlat($color: $euiShadowColor) { + box-shadow: + 0 0 .8px rgba($color, shadowOpacity(.06)), + 0 0 2px rgba($color, shadowOpacity(.04)), + 0 0 5px rgba($color, shadowOpacity(.04)), + 0 0 17px rgba($color, shadowOpacity(.03)); +} + +@mixin euiBottomShadow($color: $euiShadowColor) { + box-shadow: + 0 1px 5px rgba($color, shadowOpacity(.1)), + 0 3.6px 13px rgba($color, shadowOpacity(.07)), + 0 8.4px 23px rgba($color, shadowOpacity(.06)), + 0 23px 35px rgba($color, shadowOpacity(.05)); +} + +@mixin euiBottomShadowLarge( + $color: $euiShadowColor, + $opacity: 0, + $reverse: false +) { + @if ($reverse) { + box-shadow: + 0 -2.7px 9px rgba($color, shadowOpacity(.13)), + 0 -9.4px 24px rgba($color, shadowOpacity(.09)), + 0 -21.8px 43px rgba($color, shadowOpacity(.08)); + } @else { + box-shadow: + 0 2.7px 9px rgba($color, shadowOpacity(.13)), + 0 9.4px 24px rgba($color, shadowOpacity(.09)), + 0 21.8px 43px rgba($color, shadowOpacity(.08)); + } +} + +@mixin euiSlightShadowHover($color: $euiShadowColor) { + box-shadow: + 0 1px 5px rgba($color, shadowOpacity(.1)), + 0 3.6px 13px rgba($color, shadowOpacity(.07)), + 0 8.4px 23px rgba($color, shadowOpacity(.06)), + 0 23px 35px rgba($color, shadowOpacity(.05)); +} + +// stylelint-disable color-named +@mixin euiOverflowShadow($direction: 'y', $side: 'both') { + $hideHeight: $euiScrollBarCornerThin * 1.25; + $gradient: null; + $gradientStart: + transparentize(red, .9) 0%, + transparentize(red, 0) $hideHeight; + $gradientEnd: + transparentize(red, 0) calc(100% - #{$hideHeight}), + transparentize(red, .9) 100%; + @if ($side == 'both' or $side == 'start' or $side == 'end') { + @if ($side == 'both') { + $gradient: $gradientStart, $gradientEnd; + } @else if ($side == 'start') { + $gradient: $gradientStart; + } @else { + $gradient: $gradientEnd; + } + } @else { + @warn "euiOverflowShadow() expects side to be 'both', 'start' or 'end' but got '#{$side}'"; + } + + @if ($direction == 'y') { + mask-image: linear-gradient(to bottom, #{$gradient}); + } @else if ($direction == 'x') { + mask-image: linear-gradient(to right, #{$gradient}); + } @else { + @warn "euiOverflowShadow() expects direction to be 'y' or 'x' but got '#{$direction}'"; + } + + // Chrome+Edge has a very bizarre edge case bug where `mask-image` stops working + // This workaround forces a stacking context on the scrolling container, which + // hopefully addresses the bug. @see https://github.com/elastic/eui/pull/7855 + transform: translateZ(0); +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_size.scss b/packages/eui-theme-common/src/global_styling/mixins/_size.scss new file mode 100644 index 00000000000..809dc870bf8 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_size.scss @@ -0,0 +1,4 @@ +@mixin size($size) { + width: $size; + height: $size; +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_states.scss b/packages/eui-theme-common/src/global_styling/mixins/_states.scss new file mode 100644 index 00000000000..a2d1bc83aef --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_states.scss @@ -0,0 +1,50 @@ +@mixin euiFocusRing($size: 'small', $amsterdamOnlyProp: null) { + @if $size == 'large' { + // It's always OK to use the focus animation. This will take precedence over times we turn it off individually like EuiButtonEmpty + // stylelint-disable-next-line declaration-no-important + animation: $euiAnimSpeedSlow $euiAnimSlightResistance 1 normal forwards focusRingAnimateLarge !important; + } @else { + // stylelint-disable-next-line declaration-no-important + animation: $euiAnimSpeedSlow $euiAnimSlightResistance 1 normal forwards focusRingAnimate !important; + } +} + +// Keyframe animation declarations can be found in +// utility/animations.scss + +@mixin euiFocusBackground($color: $euiColorPrimary) { + background-color: tintOrShade($euiColorPrimary, ((1 - $euiFocusTransparency) * 100%), ((1 - $euiFocusTransparency) * 100%)); +} + +@mixin euiHoverState { + cursor: pointer; + text-decoration: underline; +} + +@mixin euiFocusState($color: $euiColorPrimary) { + @include euiHoverState; + @include euiFocusBackground($color); +} + +@mixin euiDisabledState($color: $euiButtonColorDisabledText) { + cursor: not-allowed; + text-decoration: none; + + @if ($color) { + color: $color; + } +} + +@mixin euiInteractiveStates($focusColor: $euiColorPrimary, $disabledColor: $euiButtonColorDisabledText) { + &:hover { + @include euiHoverState; + } + + &:focus { + @include euiFocusState($focusColor); + } + + &:disabled { + @include euiDisabledState($disabledColor); + } +} diff --git a/packages/eui-theme-common/src/global_styling/mixins/_tool_tip.scss b/packages/eui-theme-common/src/global_styling/mixins/_tool_tip.scss new file mode 100644 index 00000000000..d8feb0d4258 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_tool_tip.scss @@ -0,0 +1,25 @@ +@mixin euiToolTipStyle($size: null) { + @include euiBottomShadow($color: $euiColorInk); + + $euiTooltipBackgroundColor: tintOrShade($euiColorFullShade, 25%, 100%) !default; + $euiTooltipBorderColor: tintOrShade($euiColorFullShade, 35%, 80%) !default; + + border-radius: $euiBorderRadius; + background-color: $euiTooltipBackgroundColor; + color: $euiColorGhost; + z-index: $euiZLevel9; + max-width: 256px; + overflow-wrap: break-word; + padding: $euiSizeS; + + .euiHorizontalRule { + background-color: $euiTooltipBorderColor; + } +} + +@mixin euiToolTipTitle { + font-weight: $euiFontWeightBold; + border-bottom: solid $euiBorderWidthThin $euiTooltipBorderColor; + padding-bottom: $euiSizeXS; + margin-bottom: $euiSizeXS; +} \ No newline at end of file diff --git a/packages/eui-theme-common/src/global_styling/mixins/_typography.scss b/packages/eui-theme-common/src/global_styling/mixins/_typography.scss new file mode 100644 index 00000000000..7090f0e2371 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/mixins/_typography.scss @@ -0,0 +1,167 @@ +// stylelint-disable property-no-vendor-prefix +// stylelint-disable declaration-no-important + +@function fontSizeToRemOrEm($size, $sizingMethod: 'rem') { + @if ($sizingMethod == 'rem') { + @return #{$size / $euiFontSize}rem; + } @else if ($sizingMethod == 'em') { + @return #{$size / $euiFontSize}em; + } +} + +// It can also be applied to calculate paddings +@function marginToRemOrEm($elementSize, $elementFontSize, $sizingMethod: 'rem') { + @if ($sizingMethod == 'rem') { + @return #{$elementSize / $euiFontSize}rem; + } @else if ($sizingMethod == 'em') { + @return #{$elementSize / $elementFontSize}em; + } +} + +// Spit out rem and px +@mixin fontSize($size: $euiFontSize, $sizingMethod: 'rem') { + @if ($sizingMethod == 'rem') { + font-size: $size; + font-size: fontSizeToRemOrEm($size, 'rem'); + } @else if ($sizingMethod == 'em') { + font-size: fontSizeToRemOrEm($size, 'em'); + } +} + +@mixin lineHeightFromBaseline($multiplier: 3) { + line-height: lineHeightFromBaseline($multiplier); +} + +// Some mixins that help us deal with browser scaling of text more consistently. +// Essentially, fonts across eui should scale against the root html element, not +// against parent inheritance. + +// Our base fonts + +@mixin euiFont { + font-family: $euiFontFamily; + font-weight: $euiFontWeightRegular; + letter-spacing: -.005em; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + font-kerning: normal; +} + +@mixin euiCodeFont { + font-family: $euiCodeFontFamily; + letter-spacing: normal; +} + +@mixin euiText { + color: $euiTextColor; + font-weight: $euiFontWeightRegular; +} + +@mixin euiTitle($size: 'm') { + @include euiTextBreakWord; + color: $euiTitleColor; + + @if (map-has-key($euiTitles, $size)) { + @each $property, $value in map-get($euiTitles, $size) { + @if ($property == 'font-size') { + @include fontSize($value); + } @else { + #{$property}: $value; + } + } + } @else { + @include fontSize($size); + @include lineHeightFromBaseline(3); + } +} + +// Font sizing extends, using rem mixin + +@mixin euiFontSizeXS { + @include fontSize($euiFontSizeXS); + line-height: $euiLineHeight; +} + +@mixin euiFontSizeS { + @include fontSize($euiFontSizeS); + line-height: $euiLineHeight; +} + +@mixin euiFontSize { + @include fontSize($euiFontSize); + line-height: $euiLineHeight; +} + +@mixin euiFontSizeM { + @include fontSize($euiFontSizeM); + line-height: $euiLineHeight; +} + +@mixin euiFontSizeL { + @include fontSize($euiFontSizeL); + line-height: $euiLineHeight; +} + +@mixin euiFontSizeXL { + @each $property, $value in map-get($euiTitles, 'm') { + @if ($property == 'font-size') { + @include fontSize($value); + } @else { + #{$property}: $value; + } + } + line-height: 1.25; +} + +@mixin euiFontSizeXXL { + @each $property, $value in map-get($euiTitles, 'l') { + @if ($property == 'font-size') { + @include fontSize($value); + } @else { + #{$property}: $value; + } + } + line-height: 1.25; +} + +@mixin euiTextBreakWord { + // https://css-tricks.com/snippets/css/prevent-long-urls-from-breaking-out-of-container/ + overflow-wrap: break-word !important; // makes sure the long string will wrap and not bust out of the container + word-break: break-word; +} + +/** + * Text truncation + * + * Prevent text from wrapping onto multiple lines, and truncate with an ellipsis. + * + * 1. Ensure that the node has a maximum width after which truncation can occur. + */ +@mixin euiTextTruncate { + max-width: 100%; // 1 + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; +} + +@mixin euiNumberFormat { + font-feature-settings: $euiFontFeatureSettings, 'tnum' 1; // Fixed-width numbers for tabular data +} + +/** + * Text weight shifting + * + * When changing the font-weight based the state of the component + * this mixin will ensure that the sizing is dependent on the boldest + * weight so it doesn't shifter sibling content. + */ +@mixin euiTextShift($fontWeight: $euiFontWeightBold, $attr: 'data-text') { + &::after { + display: block; + content: attr(#{$attr}); + font-weight: $fontWeight; + height: 0; + overflow: hidden; + visibility: hidden; + } +} diff --git a/packages/eui-theme-common/src/global_styling/react_date_picker/_date_picker.scss b/packages/eui-theme-common/src/global_styling/react_date_picker/_date_picker.scss new file mode 100644 index 00000000000..84208a3a648 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/react_date_picker/_date_picker.scss @@ -0,0 +1,772 @@ +/* This file is a heavy retheme of react-datepicker's Sass as of v1.4.0 +** https://github.com/Hacker0x01/react-datepicker +** +** In places where features were disabled, I've commented out the original Sass +** selectors rather than removing it so we can better understand what's changed. +** Commented out selectors that don't have properties indicate that we are not +** using those dom elements for styling of any kind. For example, react-datepicker +** has lots of pointer arrows attached to its popovers, but we choose not to render +** then in any way. +** +** Similarly, you will also find several times where we use display: none to +** completely remove extraneous UI (they had some overly obvious legends for example). +*/ + + +// Because we don't have control over react-datepicker's dom we use SVG URIs for the navigation arrows. +// There is one for light and dark. +@mixin datePickerArrow { + background-position: center; + @if (lightness($euiColorEmptyShade) > 50) { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiI+ICA8ZGVmcz4gICAgPHBhdGggaWQ9ImFycm93X2Rvd24tYSIgZD0iTTEzLjA2ODg1MDgsNS4xNTcyNTAzOCBMOC4zODQyMzk3NSw5Ljc2ODI3NDI4IEM4LjE3MDU0NDE1LDkuOTc4NjEzMDggNy44Mjk5OTIxNCw5Ljk3OTE0MDk1IDcuNjE1NzYwMjUsOS43NjgyNzQyOCBMMi45MzExNDkxNSw1LjE1NzI1MDM4IEMyLjcxODEzNTksNC45NDc1ODMyMSAyLjM3Mjc3MzE5LDQuOTQ3NTgzMjEgMi4xNTk3NTk5NCw1LjE1NzI1MDM4IEMxLjk0Njc0NjY5LDUuMzY2OTE3NTYgMS45NDY3NDY2OSw1LjcwNjg1NTIyIDIuMTU5NzU5OTQsNS45MTY1MjI0IEw2Ljg0NDM3MTA0LDEwLjUyNzU0NjMgQzcuNDg1MTc0MjQsMTEuMTU4MjgzNiA4LjUxNjQ0OTc5LDExLjE1NjY4NTEgOS4xNTU2Mjg5NiwxMC41Mjc1NDYzIEwxMy44NDAyNDAxLDUuOTE2NTIyNCBDMTQuMDUzMjUzMyw1LjcwNjg1NTIyIDE0LjA1MzI1MzMsNS4zNjY5MTc1NiAxMy44NDAyNDAxLDUuMTU3MjUwMzggQzEzLjYyNzIyNjgsNC45NDc1ODMyMSAxMy4yODE4NjQxLDQuOTQ3NTgzMjEgMTMuMDY4ODUwOCw1LjE1NzI1MDM4IFoiLz4gIDwvZGVmcz4gIDxnIGZpbGwtcnVsZT0iZXZlbm9kZCI+ICAgIDx1c2UgZmlsbC1ydWxlPSJub256ZXJvIiB4bGluazpocmVmPSIjYXJyb3dfZG93bi1hIi8+ICA8L2c+PC9zdmc+); + } @else { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiI+ICA8ZGVmcz4gICAgPHBhdGggaWQ9ImRvd25hcnJvd3doaXRlLWEiIGQ9Ik0xMy4wNjg4NTA4LDUuMTU3MjUwMzggTDguMzg0MjM5NzUsOS43NjgyNzQyOCBDOC4xNzA1NDQxNSw5Ljk3ODYxMzA4IDcuODI5OTkyMTQsOS45NzkxNDA5NSA3LjYxNTc2MDI1LDkuNzY4Mjc0MjggTDIuOTMxMTQ5MTUsNS4xNTcyNTAzOCBDMi43MTgxMzU5LDQuOTQ3NTgzMjEgMi4zNzI3NzMxOSw0Ljk0NzU4MzIxIDIuMTU5NzU5OTQsNS4xNTcyNTAzOCBDMS45NDY3NDY2OSw1LjM2NjkxNzU2IDEuOTQ2NzQ2NjksNS43MDY4NTUyMiAyLjE1OTc1OTk0LDUuOTE2NTIyNCBMNi44NDQzNzEwNCwxMC41Mjc1NDYzIEM3LjQ4NTE3NDI0LDExLjE1ODI4MzYgOC41MTY0NDk3OSwxMS4xNTY2ODUxIDkuMTU1NjI4OTYsMTAuNTI3NTQ2MyBMMTMuODQwMjQwMSw1LjkxNjUyMjQgQzE0LjA1MzI1MzMsNS43MDY4NTUyMiAxNC4wNTMyNTMzLDUuMzY2OTE3NTYgMTMuODQwMjQwMSw1LjE1NzI1MDM4IEMxMy42MjcyMjY4LDQuOTQ3NTgzMjEgMTMuMjgxODY0MSw0Ljk0NzU4MzIxIDEzLjA2ODg1MDgsNS4xNTcyNTAzOCBaIi8+ICA8L2RlZnM+ICA8ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPiAgICA8dXNlIGZpbGw9IiNGRkYiIGZpbGwtcnVsZT0ibm9uemVybyIgeGxpbms6aHJlZj0iI2Rvd25hcnJvd3doaXRlLWEiLz4gIDwvZz48L3N2Zz4=); + } +} + +// The only "new" css in this component is a wrapper class for dealing with shadows. +// This is mostly here so that we can provide an inline version that doesn't have the +// shadows and depth. +.euiDatePicker { + .euiFormControlLayout { + height: auto; + } + + &.euiDatePicker--shadow { + .react-datepicker-popper { + @include euiBottomShadowMedium; + + border: $euiBorderThin; + background-color: $euiColorEmptyShade; + border-radius: 0 0 $euiBorderRadius $euiBorderRadius; + } + + // If the shadow is on, and it is inline, we need to put the shadow on the datepicker + // itself rather than the popper. + &.euiDatePicker--inline { + .react-datepicker { + @include euiBottomShadowMedium; + + border: $euiBorderThin; + background-color: $euiColorEmptyShade; + border-radius: $euiBorderRadius; + } + } + } +} + +// .react-datepicker-wrapper { +// } + +.react-datepicker { + @include euiFont; + font-size: $euiFontSizeXS; + color: $euiColorFullShade; + display: flex; + position: relative; + border-radius: $euiBorderRadius; +} + +// When in time only mode we make the dropdown look more like the combo box styling. +.react-datepicker--time-only { + + .react-datepicker__time-container { + + .react-datepicker__time { + + .react-datepicker__time-box { + width: 100%; + + .react-datepicker__time-list li.react-datepicker__time-list-item { + font-size: $euiFontSizeS; + text-align: left; + padding-left: $euiSizeXL + $euiSizeXS; + padding-right: $euiSizeXL + $euiSizeXS; + color: $euiTextColor; + + &.react-datepicker__time-list-item--selected { + color: $euiColorGhost; + } + + &.react-datepicker__time-list-item--disabled{ + color: $euiColorDisabledText + } + } + } + } + } + + + .react-datepicker__time-container { + border-left: 0; + } + + // .react-datepicker__triangle { + // } + // .react-datepicker__time { + // } + // .react-datepicker__time-box { + // } +} + +// .react-datepicker__triangle { +// } + +.euiDatePicker.euiDatePicker--shadow .react-datepicker-popper { + z-index: $euiZContentMenu; + animation: euiAnimFadeIn $euiAnimSpeedFast ease-in; + + &[data-placement^="bottom"] { + + // .react-datepicker__triangle { + // } + } + + &[data-placement^="top"] { + @include euiBottomShadowFlat; + + border-radius: $euiBorderRadius $euiBorderRadius 0 0; + + // .react-datepicker__triangle { + // } + } + + &[data-placement^="right"] { + margin-left: 0; + + // .react-datepicker__triangle { + // } + } + + &[data-placement^="left"] { + margin-right: 0; + + // .react-datepicker__triangle { + // } + } +} + +.react-datepicker__header { + text-align: center; + border-top-left-radius: $euiBorderRadius; + border-top-right-radius: $euiBorderRadius; + + &--time { + display: none; + } +} + +.react-datepicker__header__dropdown { + padding: $euiSize 0 $euiSizeS 0; +} + +.react-datepicker__year-dropdown-container--select, +.react-datepicker__month-dropdown-container--select, +.react-datepicker__month-year-dropdown-container--select, +.react-datepicker__year-dropdown-container--scroll, +.react-datepicker__month-dropdown-container--scroll, +.react-datepicker__month-year-dropdown-container--scroll { + display: inline-block; + margin: 0 $euiSizeXS; +} + +.react-datepicker__current-month, +.react-datepicker-time__header { + display: none; +} + +.react-datepicker-time__header { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + +.react-datepicker__navigation { + cursor: pointer; + position: absolute; + // Pixel pushing because these are icons against text + top: $euiSize + ($euiSizeXS / 2); + width: 0; + padding: 0; + z-index: 1; + text-indent: -999em; + overflow: hidden; + + &--previous { + @include datePickerArrow; + left: $euiSize + $euiSizeXS; + height: $euiSize; + width: $euiSize; + transform: rotate(90deg); + transition: transform $euiAnimSpeedExtraFast ease-in-out; + + &:hover, &:focus { + border-radius: $euiBorderRadius; + transform: scale(1.2) rotate(90deg); + } + + &:hover { + background-color: $euiColorLightestShade; + box-shadow: 0 0 0 ($euiSizeXS / 2) $euiColorLightestShade; + } + + &:focus { + background-color: $euiFocusBackgroundColor; + box-shadow: 0 0 0 ($euiSizeXS / 2) $euiFocusBackgroundColor; + } + + &--disabled, + &--disabled:hover { + cursor: not-allowed ; + opacity: .2; + } + } + + &--next { + @include datePickerArrow; + // Pixel value because of some padding on the icon + right: 20px; + height: $euiSize; + width: $euiSize; + transform: rotate(-90deg); + + &--with-time:not(&--with-today-button) { + // This a pixel value against the width of the cal. It needs + // to be left because the timepicker adds more width + left: 248px; + } + + &:hover, &:focus { + border-radius: $euiBorderRadius; + transform: scale(1.2) rotate(-90deg); + } + + &:hover { + background-color: $euiColorLightestShade; + box-shadow: 0 0 0 ($euiSizeXS / 2) $euiColorLightestShade; + } + + &:focus { + background-color: $euiFocusBackgroundColor; + box-shadow: 0 0 0 ($euiSizeXS / 2) $euiFocusBackgroundColor; + } + + &--disabled, + &--disabled:hover { + cursor: not-allowed ; + opacity: .2; + } + } + + &--years { + position: relative; + top: 0; + display: block; + margin-left: auto; + margin-right: auto; + + &-previous { + top: $euiSizeXS; + border-top-color: $euiColorLightestShade; + + &:hover { + border-top-color: darken($euiColorLightestShade, 10%); + } + } + + &-upcoming { + top: -$euiSizeXS; + border-bottom-color: $euiColorLightestShade; + + &:hover { + border-bottom-color: darken($euiColorLightestShade, 10%); + } + } + } +} + +// .react-datepicker__month-container { +// } + +.react-datepicker__month { + margin: 0 $euiSize $euiSize $euiSize; + text-align: center; + border-radius: $euiBorderRadius; +} + +.react-datepicker__time-container { + border-left: $euiBorderColor; + width: auto; + display: flex; + padding: $euiSize 0; + border-radius: 0 $euiBorderRadius $euiBorderRadius 0; + flex-grow: 1; + + // &--with-today-button { + // } + + .react-datepicker__time { + position: relative; + flex-grow: 1; + display: flex; + padding-left: $euiSizeXS; + flex-direction: column; + + .react-datepicker__time-box { + width: auto; + display: flex; + flex-direction: column; + flex-grow: 1; + + ul.react-datepicker__time-list { + @include euiScrollBar; + height: 204px !important; + display: flex; + flex-direction: column; + flex-grow: 1; + overflow-y: auto; + align-items: center; + + li.react-datepicker__time-list-item { + padding: $euiSizeXS $euiSizeS; + margin-bottom: $euiSizeXS; + text-align: right; + color: $euiColorDarkShade; + white-space: nowrap; + // IE needs this to fix collapsing flex + line-height: $euiSizeM; + + &:hover, + &:focus { + cursor: pointer; + text-decoration: underline; + } + &--selected { + background-color: $euiColorPrimary; + color: white; + border-radius: $euiBorderRadius / 2; + &:hover { + background-color: $euiColorPrimary; + } + } + &--disabled { + color: $euiColorLightShade; + + &:hover { + cursor: not-allowed; + text-decoration: none; + background-color: transparent; + } + } + } + } + } + } +} + +.react-datepicker__week-number { + color: $euiColorLightestShade; + display: inline-block; + width: $euiSizeXL; + line-height: $euiSizeXL - $euiSizeXS; + text-align: center; + margin: 0 $euiSizeXS; + &.react-datepicker__week-number--clickable { + cursor: pointer; + &:hover { + border-radius: $euiBorderRadius; + background-color: $euiColorEmptyShade; + } + } +} + +.react-datepicker__day-names, +.react-datepicker__week { + white-space: nowrap; +} + +.react-datepicker__day-name, +.react-datepicker__day, +.react-datepicker__time-name { + color: $euiColorFullShade; + display: inline-block; + width: $euiSizeXL; + line-height: $euiSizeXL - $euiSizeXS; + text-align: center; + margin: 0 $euiSizeXS / 2; +} + +.react-datepicker__day-name { + color: $euiColorDarkShade; + text-transform: uppercase; +} + +.react-datepicker__day { + cursor: pointer; + border: solid 2px transparent; + transition: transform $euiAnimSpeedExtraFast ease-in-out; + + &:hover:not(&--disabled) { + text-decoration: underline; + font-weight: $euiFontWeightBold; + transform: scale(1.2); + } + + &--today { + font-weight: bold; + color: $euiColorPrimary; + } + &--outside-month { + color: $euiColorDarkShade; + } + + &--highlighted { + border-radius: $euiBorderRadius; + background-color: $euiColorSuccess; + color: $euiColorGhost; + + &:hover { + background-color: darken($euiColorSuccess, 5%); + } + } + + &--in-range { + background-color: transparentize($euiColorPrimary, .9); + color: $euiColorFullShade; + border-radius: 0; + border-top: solid 6px $euiColorEmptyShade; + border-bottom: solid 6px $euiColorEmptyShade; + border-right: none; + border-left: none; + line-height: $euiSizeL - $euiSizeXS; + } + + &--selected, + &--in-selecting-range { + height: $euiSizeXL; + margin: 0 $euiSizeXS / 2; + border-radius: $euiBorderRadius; + background-color: $euiColorPrimary; + line-height: $euiSizeL + $euiSizeXS; + border: solid $euiSizeXS / 2 $euiColorPrimary; + color: $euiColorGhost; + + &:hover { + background-color: darken($euiColorPrimary, 5%); + } + } + + &--keyboard-selected { + border-radius: $euiBorderRadius; + border: solid $euiSizeXS / 2 $euiColorPrimary; + font-weight: $euiFontWeightBold; + + &:hover { + background-color: darken($euiColorPrimary, 5%); + color: $euiColorGhost; + } + } + + &--in-selecting-range:not(&--in-range) { + background-color: rgba($euiColorPrimary, 0.5); + } + + &--in-range:not(&--in-selecting-range) { + .react-datepicker__month--selecting-range & { + background-color: $euiColorEmptyShade; + color: $euiColorFullShade; + } + } + + &--disabled { + cursor: not-allowed; + color: $euiColorLightShade; + + &:hover { + background-color: transparent; + } + } +} + +.react-datepicker__input-container { + position: relative; +} + +.react-datepicker__year-read-view { + font-weight: $euiFontWeightLight; + color: $euiColorDarkShade; +} + +.react-datepicker__month-read-view { + font-weight: $euiFontWeightMedium; +} + +.react-datepicker__year-read-view, +.react-datepicker__month-read-view, +.react-datepicker__month-year-read-view { + font-size: $euiFontSizeL; + + &:hover { + cursor: pointer; + color: $euiColorPrimary; + + .react-datepicker__year-read-view--down-arrow, + .react-datepicker__month-read-view--down-arrow { + border-top-color: darken($euiColorLightestShade, 10%); + } + } + + &--down-arrow { + display: none; + } +} + +.react-datepicker__year-dropdown, +.react-datepicker__month-dropdown, +.react-datepicker__month-year-dropdown { + background-color: $euiColorEmptyShade; + position: absolute; + width: 100%; + height: 100%; + left: 0; + top: 0; + bottom: 0; + right: 0; + z-index: 1; + text-align: center; + border-radius: $euiBorderRadius; + display: flex; + flex-wrap: wrap; + animation: euiAnimFadeIn $euiAnimSpeedFast ease-in; + align-content: space-around; + align-items: center; + padding: $euiSizeS; + + + &:hover { + cursor: pointer; + } + + // &--scrollable { + // height: 150px; + // overflow-y: scroll; + // } +} + +// Strike that, reverse it Willy Wonka style +.react-datepicker__year-dropdown { + flex-wrap: wrap-reverse; + flex-direction: row-reverse; + justify-content: flex-end; +} + +.react-datepicker__year-option, +.react-datepicker__month-option, +.react-datepicker__month-year-option { + font-size: $euiFontSizeXS; + padding: $euiSizeS; + color: $euiColorDarkestShade; + flex-basis: 33.3%; + + &:first-of-type { + border-top-left-radius: $euiBorderRadius; + border-top-right-radius: $euiBorderRadius; + } + + &:last-of-type { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + border-bottom-left-radius: $euiBorderRadius; + border-bottom-right-radius: $euiBorderRadius; + } + + &:hover { + background-color: $euiColorLightestShade; + + .react-datepicker__navigation--years-upcoming { + border-bottom-color: darken($euiColorLightestShade, 10%); + } + + .react-datepicker__navigation--years-previous { + border-top-color: darken($euiColorLightestShade, 10%); + } + } + + &--selected { + display: none; + } +} + +.react-datepicker__screenReaderOnly { + position: absolute; + left: -10000px; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; +} + +.react-datepicker__year-option--preselected, +.react-datepicker__month-option--preselected { + background: $euiFocusBackgroundColor; +} + +.react-datepicker__year-option--selected_year.react-datepicker__year-option--preselected, +.react-datepicker__month-option--selected_month.react-datepicker__month-option--preselected{ + background: $euiColorPrimary; +} + +.react-datepicker__time-list-item--preselected, +.react-datepicker__year-option--preselected, +.react-datepicker__month-option--preselected { + background: darken($euiFocusBackgroundColor, 5%); +} + +.react-datepicker__time-container--focus { + background: $euiFocusBackgroundColor; +} + +.react-datepicker__month-read-view:focus, +.react-datepicker__year-read-view:focus { + text-decoration: underline; +} + +.react-datepicker__month--accessible:focus { + background: $euiFocusBackgroundColor; + + .react-datepicker__day--in-range:not(.react-datepicker__day--selected) { + border-top-color: $euiFocusBackgroundColor; + border-bottom-color: $euiFocusBackgroundColor; + } +} +.react-datepicker__navigation:focus { + background-color: $euiFocusBackgroundColor; +} + +// These selectors are not a typo. react-datepicker has a bug where these selectors +// output as "--selected_year". Sass has trouble compiling .--selected_year, so instead +// we use this generic selector get around it. +.react-datepicker__year-option--selected_year, +.react-datepicker__month-option--selected_month { + background: $euiColorPrimary; + color: $euiColorEmptyShade; + font-weight: $euiFontWeightBold; + border-radius: $euiBorderRadius; +} + +.react-datepicker__focusTrap { + display: flex; +} + +// The below is for the portal version of react-datepicker which we do not use. +// It is shown here just to know what their baseline includes. + +// .react-datepicker__close-icon { +// background-color: transparent; +// border: 0; +// cursor: pointer; +// display: inline-block; +// height: 0; +// outline: 0; +// padding: 0; +// vertical-align: middle; +// +// &::after { +// background-color: $euiColorPrimary; +// border-radius: 50%; +// bottom: 0; +// box-sizing: border-box; +// color: #fff; +// content: "\00d7"; +// cursor: pointer; +// font-size: 12px; +// height: 16px; +// width: 16px; +// line-height: 1; +// margin: -8px auto 0; +// padding: 2px; +// position: absolute; +// right: 7px; +// text-align: center; +// top: 50%; +// } +// } +// +// .react-datepicker__today-button { +// background: $euiColorEmptyShade; +// border-top: 1px solid $euiBorderColor; +// cursor: pointer; +// text-align: center; +// font-weight: bold; +// padding: 5px 0; +// clear: left; +// } +// +// .react-datepicker__portal { +// position: fixed; +// width: 100vw; +// height: 100vh; +// background-color: rgba(0, 0, 0, 0.8); +// left: 0; +// top: 0; +// justify-content: center; +// align-items: center; +// display: flex; +// z-index: 2147483647; +// +// .react-datepicker__day-name, +// .react-datepicker__day, +// .react-datepicker__time-name { +// width: 3rem; +// line-height: 3rem; +// } +// +// // Resize for small screens +// @media (max-width: 400px), (max-height: 550px) { +// .react-datepicker__day-name, +// .react-datepicker__day, +// .react-datepicker__time-name { +// width: 2rem; +// line-height: 2rem; +// } +// } +// +// .react-datepicker__current-month, +// .react-datepicker-time__header { +// font-size: $euiFontSizeXS * 1.8; +// } +// +// .react-datepicker__navigation { +// border: 1.8 * $euiSize solid transparent; +// } +// +// .react-datepicker__navigation--previous { +// border-right-color: $euiColorLightestShade; +// +// &:hover { +// border-right-color: darken($euiColorLightestShade, 10%); +// } +// +// &--disabled, +// &--disabled:hover { +// border-right-color: $datepicker__navigation-disabled-color; +// cursor: default; +// } +// } +// +// .react-datepicker__navigation--next { +// border-left-color: $euiColorLightestShade; +// +// &:hover { +// border-left-color: darken($euiColorLightestShade, 10%); +// } +// +// &--disabled, +// &--disabled:hover { +// border-left-color: $datepicker__navigation-disabled-color; +// cursor: default; +// } +// } +// } diff --git a/packages/eui-theme-common/src/global_styling/react_date_picker/_index.scss b/packages/eui-theme-common/src/global_styling/react_date_picker/_index.scss new file mode 100644 index 00000000000..48ea2f99771 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/react_date_picker/_index.scss @@ -0,0 +1,2 @@ +@import 'variables'; +@import 'date_picker'; diff --git a/packages/eui-theme-common/src/global_styling/react_date_picker/_variables.scss b/packages/eui-theme-common/src/global_styling/react_date_picker/_variables.scss new file mode 100644 index 00000000000..6a5abd4e528 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/react_date_picker/_variables.scss @@ -0,0 +1 @@ +$euiDatePickerCalendarWidth: 284px; \ No newline at end of file diff --git a/packages/eui-theme-common/src/global_styling/types.ts b/packages/eui-theme-common/src/global_styling/types.ts new file mode 100644 index 00000000000..0b8ae557a7c --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/types.ts @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { CSSObject } from '@emotion/react'; + +import type { RecursivePartial, ValueOf } from '../types'; + +import { _EuiThemeAnimation } from './variables/animations'; +import { _EuiThemeBreakpoints } from './variables/breakpoint'; +import { _EuiThemeBorder } from './variables/borders'; +import { _EuiThemeColors } from './variables/colors'; +import { _EuiThemeBase, _EuiThemeSizes } from './variables/size'; +import { _EuiThemeFont } from './variables/typography'; +import { _EuiThemeFocus } from './variables/states'; +import { _EuiThemeLevels } from './variables/levels'; +import { _EuiThemeComponents } from './variables/components'; + +export const COLOR_MODES_STANDARD = { + light: 'LIGHT', + dark: 'DARK', +} as const; +export const COLOR_MODES_INVERSE = 'INVERSE' as const; + +export type EuiThemeColorModeInverse = typeof COLOR_MODES_INVERSE; +export type EuiThemeColorModeStandard = ValueOf; +export type EuiThemeColorMode = + | 'light' + | 'dark' + | EuiThemeColorModeStandard + | 'inverse' + | EuiThemeColorModeInverse; + +export type ColorModeSwitch = + | { + [key in EuiThemeColorModeStandard]: T; + } + | T; + +export type StrictColorModeSwitch = { + [key in EuiThemeColorModeStandard]: T; +}; + +export type EuiThemeShape = { + colors: _EuiThemeColors; + /** - Default value: 16 */ + base: _EuiThemeBase; + /** + * @see {@link https://eui.elastic.co/#/theming/sizing | Reference} for more information + */ + size: _EuiThemeSizes; + font: _EuiThemeFont; + border: _EuiThemeBorder; + focus: _EuiThemeFocus; + animation: _EuiThemeAnimation; + breakpoint: _EuiThemeBreakpoints; + levels: _EuiThemeLevels; + // bevel: _EuiThemeBevel; + components: _EuiThemeComponents; +}; + +export type EuiThemeSystem = { + root: EuiThemeShape & T; + model: EuiThemeShape & T; + key: string; +}; + +export type EuiThemeModifications = RecursivePartial; + +export type ComputedThemeShape< + T, + P = string | number | bigint | boolean | null | undefined +> = T extends P | ColorModeSwitch + ? T extends ColorModeSwitch + ? X extends P + ? X + : { + [K in keyof (X & + Exclude< + T, + keyof X | keyof StrictColorModeSwitch + >)]: ComputedThemeShape< + (X & Exclude)[K], + P + >; + } + : T + : { + [K in keyof T]: ComputedThemeShape; + }; + +export type EuiThemeComputed = ComputedThemeShape & { + themeName: string; +}; + +export type EuiThemeNested = { + isGlobalTheme: boolean; + hasDifferentColorFromGlobalTheme: boolean; + bodyColor: string; + colorClassName: string; + setGlobalCSSVariables: Function; + globalCSSVariables?: CSSObject; + setNearestThemeCSSVariables: Function; + themeCSSVariables?: CSSObject; +}; diff --git a/packages/eui-theme-common/src/global_styling/utility/_animations.scss b/packages/eui-theme-common/src/global_styling/utility/_animations.scss new file mode 100644 index 00000000000..c0cb6e7365a --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/utility/_animations.scss @@ -0,0 +1,55 @@ +// Animations as utility so they don't get duplicated in compiled CSS + +@keyframes euiAnimFadeIn { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes euiGrow { + 0% { + opacity: 0; + } + + 1% { + opacity: 0; + transform: scale(0); + } + + 100% { + opacity: 1; + transform: scale(1); + } +} + +@keyframes focusRingAnimate { + 0% { + box-shadow: 0 0 0 $euiFocusRingAnimStartSize $euiFocusRingAnimStartColor; + } + + 100% { + box-shadow: 0 0 0 $euiFocusRingSize $euiFocusRingColor; + } +} + +@keyframes focusRingAnimateLarge { + 0% { + box-shadow: 0 0 0 $euiFocusRingAnimStartSizeLarge $euiFocusRingAnimStartColor; + } + + 100% { + box-shadow: 0 0 0 $euiFocusRingSizeLarge $euiFocusRingColor; + } +} + +// Component specific + +@keyframes euiButtonActive { + 50% { + transform: translateY(1px); + } +} diff --git a/packages/eui-theme-common/src/global_styling/utility/_index.scss b/packages/eui-theme-common/src/global_styling/utility/_index.scss new file mode 100644 index 00000000000..5c978807848 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/utility/_index.scss @@ -0,0 +1 @@ +@import 'animations'; diff --git a/packages/eui-theme-common/src/global_styling/variables/_animations.scss b/packages/eui-theme-common/src/global_styling/variables/_animations.scss new file mode 100644 index 00000000000..93b9daf1641 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_animations.scss @@ -0,0 +1,13 @@ +// Animations + +$euiAnimSlightBounce: cubic-bezier(.34, 1.61, .7, 1) !default; +$euiAnimSlightResistance: cubic-bezier(.694, .0482, .335, 1) !default; + +$euiAnimSpeedExtraFast: 90ms !default; +$euiAnimSpeedFast: 150ms !default; +$euiAnimSpeedNormal: 250ms !default; +$euiAnimSpeedSlow: 350ms !default; +$euiAnimSpeedExtraSlow: 500ms !default; + +// Keyframe animation declarations can be found in +// eui/utility/animations.scss diff --git a/packages/eui-theme-common/src/global_styling/variables/_borders.scss b/packages/eui-theme-common/src/global_styling/variables/_borders.scss new file mode 100644 index 00000000000..6fa0216ff3b --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_borders.scss @@ -0,0 +1,11 @@ +// Borders + +$euiBorderWidthThin: 1px !default; +$euiBorderWidthThick: 2px !default; + +$euiBorderColor: $euiColorLightShade !default; +$euiBorderRadius: $euiSizeS * .75 !default; +$euiBorderRadiusSmall: $euiSizeS * .5 !default; +$euiBorderThick: $euiBorderWidthThick solid $euiBorderColor !default; +$euiBorderThin: $euiBorderWidthThin solid $euiBorderColor !default; +$euiBorderEditable: $euiBorderWidthThick dotted $euiBorderColor !default; diff --git a/packages/eui-theme-common/src/global_styling/variables/_buttons.scss b/packages/eui-theme-common/src/global_styling/variables/_buttons.scss new file mode 100644 index 00000000000..4d4e8a5f0b1 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_buttons.scss @@ -0,0 +1,18 @@ +$euiButtonHeight: $euiSizeXXL !default; +$euiButtonHeightSmall: $euiSizeXL !default; +$euiButtonHeightXSmall: $euiSizeL !default; + +// Modifier naming and colors. +$euiButtonTypes: ( + primary: $euiColorPrimary, + accent: $euiColorAccent, + success: $euiColorSuccess, + warning: $euiColorWarning, + danger: $euiColorDanger, + ghost: $euiColorGhost, // Ghost is special, and does not care about theming. + text: $euiColorDarkShade, // Reserved for special use cases +) !default; + +// TODO: Remove this once elastic-charts no longer uses this variable +// @see https://github.com/elastic/elastic-charts/pull/2528 +$euiButtonColorDisabledText: $euiColorDisabledText; diff --git a/packages/eui-theme-common/src/global_styling/variables/_colors_vis.scss b/packages/eui-theme-common/src/global_styling/variables/_colors_vis.scss new file mode 100644 index 00000000000..cfffdf5e55d --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_colors_vis.scss @@ -0,0 +1,72 @@ +// Visualization colors +// stylelint-disable color-no-hex + +// Maps allow for easier JSON usage +// Use map_merge($euiColorVisColors, $yourMap) to change individual colors after importing ths file +// The `behindText` variant is a direct copy of the hex output by the JS euiPaletteColorBlindBehindText() function +$euiPaletteColorBlind: ( + euiColorVis0: ( + graphic: #54B399, + behindText: #6DCCB1, + ), + euiColorVis1: ( + graphic: #6092C0, + behindText: #79AAD9, + ), + euiColorVis2: ( + graphic: #D36086, + behindText: #EE789D, + ), + euiColorVis3: ( + graphic: #9170B8, + behindText: #A987D1, + ), + euiColorVis4: ( + graphic: #CA8EAE, + behindText: #E4A6C7, + ), + euiColorVis5: ( + graphic: #D6BF57, + behindText: #F1D86F, + ), + euiColorVis6: ( + graphic: #B9A888, + behindText: #D2C0A0, + ), + euiColorVis7: ( + graphic: #DA8B45, + behindText: #F5A35C, + ), + euiColorVis8: ( + graphic: #AA6556, + behindText: #C47C6C, + ), + euiColorVis9: ( + graphic: #E7664C, + behindText: #FF7E62, + ) +) !default; + +$euiPaletteColorBlindKeys: map-keys($euiPaletteColorBlind); + +$euiColorVis0: map-get(map-get($euiPaletteColorBlind, 'euiColorVis0'), 'graphic') !default; +$euiColorVis1: map-get(map-get($euiPaletteColorBlind, 'euiColorVis1'), 'graphic') !default; +$euiColorVis2: map-get(map-get($euiPaletteColorBlind, 'euiColorVis2'), 'graphic') !default; +$euiColorVis3: map-get(map-get($euiPaletteColorBlind, 'euiColorVis3'), 'graphic') !default; +$euiColorVis4: map-get(map-get($euiPaletteColorBlind, 'euiColorVis4'), 'graphic') !default; +$euiColorVis5: map-get(map-get($euiPaletteColorBlind, 'euiColorVis5'), 'graphic') !default; +$euiColorVis6: map-get(map-get($euiPaletteColorBlind, 'euiColorVis6'), 'graphic') !default; +$euiColorVis7: map-get(map-get($euiPaletteColorBlind, 'euiColorVis7'), 'graphic') !default; +$euiColorVis8: map-get(map-get($euiPaletteColorBlind, 'euiColorVis8'), 'graphic') !default; +$euiColorVis9: map-get(map-get($euiPaletteColorBlind, 'euiColorVis9'), 'graphic') !default; + +$euiColorVis0_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis0'), 'behindText') !default; +$euiColorVis1_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis1'), 'behindText') !default; +$euiColorVis2_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis2'), 'behindText') !default; +$euiColorVis3_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis3'), 'behindText') !default; +$euiColorVis4_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis4'), 'behindText') !default; +$euiColorVis5_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis5'), 'behindText') !default; +$euiColorVis6_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis6'), 'behindText') !default; +$euiColorVis7_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis7'), 'behindText') !default; +$euiColorVis8_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis8'), 'behindText') !default; +$euiColorVis9_behindText: map-get(map-get($euiPaletteColorBlind, 'euiColorVis9'), 'behindText') !default; diff --git a/packages/eui-theme-common/src/global_styling/variables/_colors_vis.ts b/packages/eui-theme-common/src/global_styling/variables/_colors_vis.ts new file mode 100644 index 00000000000..4459b04ff8c --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_colors_vis.ts @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * NOTE: These were quick conversions of their Sass counterparts. + * They have yet to be used/tested. + * TODO: Make the graphic version available from `euiPaletteColorBlind()` + */ + +// Maps allow for easier JSON usage +// Use map_merge(euiColorVisColors, $yourMap) to change individual colors after importing ths file +// The `behindText` variant is a direct copy of the hex output by the JS euiPaletteColorBlindBehindText() function +const euiPaletteColorBlind = { + euiColorVis0: { + graphic: '#54B399', + }, + euiColorVis1: { + graphic: '#6092C0', + }, + euiColorVis2: { + graphic: '#D36086', + }, + euiColorVis3: { + graphic: '#9170B8', + }, + euiColorVis4: { + graphic: '#CA8EAE', + }, + euiColorVis5: { + graphic: '#D6BF57', + }, + euiColorVis6: { + graphic: '#B9A888', + }, + euiColorVis7: { + graphic: '#DA8B45', + }, + euiColorVis8: { + graphic: '#AA6556', + }, + euiColorVis9: { + graphic: '#E7664C', + }, +}; + +export const colorVis = { + euiColorVis0: euiPaletteColorBlind.euiColorVis0.graphic, + euiColorVis1: euiPaletteColorBlind.euiColorVis1.graphic, + euiColorVis2: euiPaletteColorBlind.euiColorVis2.graphic, + euiColorVis3: euiPaletteColorBlind.euiColorVis3.graphic, + euiColorVis4: euiPaletteColorBlind.euiColorVis4.graphic, + euiColorVis5: euiPaletteColorBlind.euiColorVis5.graphic, + euiColorVis6: euiPaletteColorBlind.euiColorVis6.graphic, + euiColorVis7: euiPaletteColorBlind.euiColorVis7.graphic, + euiColorVis8: euiPaletteColorBlind.euiColorVis8.graphic, + euiColorVis9: euiPaletteColorBlind.euiColorVis9.graphic, +}; diff --git a/packages/eui-theme-common/src/global_styling/variables/_font_weight.scss b/packages/eui-theme-common/src/global_styling/variables/_font_weight.scss new file mode 100644 index 00000000000..f5dfd287835 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_font_weight.scss @@ -0,0 +1,10 @@ +// Separated out to its own file for easy import into docs + +// Font weights +$euiFontWeightLight: 300 !default; +$euiFontWeightRegular: 400 !default; +$euiFontWeightMedium: 500 !default; +$euiFontWeightSemiBold: 600 !default; +$euiFontWeightBold: 700 !default; +$euiCodeFontWeightRegular: 400 !default; +$euiCodeFontWeightBold: 700 !default; diff --git a/packages/eui-theme-common/src/global_styling/variables/_form.scss b/packages/eui-theme-common/src/global_styling/variables/_form.scss new file mode 100644 index 00000000000..41c5dfad04e --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_form.scss @@ -0,0 +1,21 @@ +// Sizing +$euiFormMaxWidth: $euiSizeXXL * 10 !default; +$euiFormControlHeight: $euiSizeXXL !default; +$euiFormControlCompressedHeight: $euiSizeXL !default; +$euiFormControlPadding: $euiSizeM !default; +$euiFormControlCompressedPadding: $euiSizeS !default; +$euiFormControlBorderRadius: $euiBorderRadius !default; +$euiFormControlCompressedBorderRadius: $euiBorderRadiusSmall !default; + +// Coloring +$euiFormBackgroundColor: tintOrShade($euiColorLightestShade, 60%, 40%) !default; +$euiFormBackgroundDisabledColor: darken($euiColorLightestShade, 2%) !default; +$euiFormBackgroundReadOnlyColor: $euiColorEmptyShade !default; +$euiFormBorderOpaqueColor: shadeOrTint(desaturate(adjust-hue($euiColorPrimary, 22), 22.95), 26%, 100%) !default; +$euiFormBorderColor: transparentize($euiFormBorderOpaqueColor, .9) !default; +$euiFormBorderDisabledColor: transparentize($euiFormBorderOpaqueColor, .9) !default; +$euiFormControlDisabledColor: $euiColorMediumShade !default; +$euiFormControlBoxShadow: 0 0 transparent !default; +$euiFormControlPlaceholderText: makeHighContrastColor($euiTextSubduedColor, $euiFormBackgroundColor) !default; +$euiFormInputGroupLabelBackground: tintOrShade($euiColorLightShade, 50%, 15%) !default; +$euiFormInputGroupBorder: none !default; \ No newline at end of file diff --git a/packages/eui-theme-common/src/global_styling/variables/_index.scss b/packages/eui-theme-common/src/global_styling/variables/_index.scss new file mode 100644 index 00000000000..993979a78a5 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_index.scss @@ -0,0 +1,23 @@ +// -------------------------------------------------------------------------------------- +// EUI global variables +// -------------------------------------------------------------------------------------- +// This module contains all global variables available within EUI. Every variable in this +// document should be prefixed with $eui. This lets us know where the variable is +// coming from when looking inside the individual component files. Any component local +// variables should be declared at the top of those documents prefixed with $componentName. + +// Global colors are established and importer per theme, before this manifest +// Import order is important. Size, vis colors, ...etc are used in other variables. +@import 'size'; +@import 'colors_vis'; +@import 'animations'; +@import 'font_weight'; +@import 'typography'; +@import 'borders'; +@import 'responsive'; +@import 'shadows'; +@import 'states'; +@import 'z_index'; + +@import 'buttons'; +@import 'form'; diff --git a/packages/eui-theme-common/src/global_styling/variables/_responsive.scss b/packages/eui-theme-common/src/global_styling/variables/_responsive.scss new file mode 100644 index 00000000000..de6e8ca5b83 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_responsive.scss @@ -0,0 +1,9 @@ +$euiBreakpoints: ( + 'xs': 0, + 's': 575px, + 'm': 768px, + 'l': 992px, + 'xl': 1200px +) !default; + +$euiBreakpointKeys: map-keys($euiBreakpoints); diff --git a/packages/eui-theme-common/src/global_styling/variables/_shadows.scss b/packages/eui-theme-common/src/global_styling/variables/_shadows.scss new file mode 100644 index 00000000000..05e445f27a1 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_shadows.scss @@ -0,0 +1,2 @@ +// Shadows +$euiShadowColor: $euiColorInk !default; diff --git a/packages/eui-theme-common/src/global_styling/variables/_size.scss b/packages/eui-theme-common/src/global_styling/variables/_size.scss new file mode 100644 index 00000000000..f07645832d1 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_size.scss @@ -0,0 +1,15 @@ +$euiSize: 16px !default; + +$euiSizeXS: $euiSize * .25 !default; +$euiSizeS: $euiSize * .5 !default; +$euiSizeM: $euiSize * .75 !default; +$euiSizeL: $euiSize * 1.5 !default; +$euiSizeXL: $euiSize * 2 !default; +$euiSizeXXL: $euiSize * 2.5 !default; + +$euiButtonMinWidth: $euiSize * 7 !default; + +$euiScrollBar: $euiSize !default; +// Corner sizes are used as an inset border and therefore a smaller corner size means a larger thumb +$euiScrollBarCorner: $euiSizeXS !default; +$euiScrollBarCornerThin: $euiSizeS * .75 !default; diff --git a/packages/eui-theme-common/src/global_styling/variables/_states.scss b/packages/eui-theme-common/src/global_styling/variables/_states.scss new file mode 100644 index 00000000000..fba4cc59caa --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_states.scss @@ -0,0 +1,14 @@ +// Colors +$euiFocusRingColor: rgba($euiColorPrimary, .3) !default; +$euiFocusRingAnimStartColor: rgba($euiColorPrimary, 0) !default; +$euiFocusRingAnimStartSize: 6px !default; +$euiFocusRingAnimStartSizeLarge: 10px !default; + +// Sizing +$euiFocusRingSizeLarge: $euiSizeXS !default; +$euiFocusRingSize: $euiFocusRingSizeLarge * .75 !default; + +// Transparency +$euiFocusTransparency: lightOrDarkTheme(.1, .2) !default; +$euiFocusTransparencyPercent: lightOrDarkTheme(90%, 80%) !default; +$euiFocusBackgroundColor: tintOrShade($euiColorPrimary, $euiFocusTransparencyPercent, $euiFocusTransparencyPercent) !default; diff --git a/packages/eui-theme-common/src/global_styling/variables/_typography.scss b/packages/eui-theme-common/src/global_styling/variables/_typography.scss new file mode 100644 index 00000000000..1ca62f3248c --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_typography.scss @@ -0,0 +1,75 @@ +// Families +$euiFontFamily: 'Inter', BlinkMacSystemFont, Helvetica, Arial, sans-serif !default; +$euiCodeFontFamily: 'Roboto Mono', Menlo, Courier, monospace !default; + +// Careful using ligatures. Code editors like ACE will often error because of width calculations +$euiFontFeatureSettings: 'calt' 1, 'kern' 1, 'liga' 1 !default; + +// Font sizes -- scale is loosely based on Major Third (1.250) +$euiTextScale: 2.25, 1.75, 1.25, 1.125, 1, .875, .75 !default; + +$euiFontSize: $euiSize !default; // 5th position in scale +$euiFontSizeXS: $euiFontSize * nth($euiTextScale, 7) !default; // 12px +$euiFontSizeS: $euiFontSize * nth($euiTextScale, 6) !default; // 14px +$euiFontSizeM: $euiFontSize * nth($euiTextScale, 4) !default; // 18px +$euiFontSizeL: $euiFontSize * nth($euiTextScale, 3) !default; // 20px +$euiFontSizeXL: $euiFontSize * nth($euiTextScale, 2) !default; // 28px +$euiFontSizeXXL: $euiFontSize * nth($euiTextScale, 1) !default; // 36px + +// Line height +$euiLineHeight: 1.5 !default; +$euiBodyLineHeight: 1 !default; + +// Normally functions are imported before variables in `_index.scss` files +// But because they need to consume some typography variables they need to live here +@function convertToRem($size) { + @return #{$size / $euiFontSize}rem; +} + +// Our base gridline is at 1/2 the font-size, ensure line-heights stay on that gridline. +// EX: A proper line-height for text is 1.5 times the font-size. +// If our base font size (euiFontSize) is 16, our baseline is 8 (16*1.5 / 3). To ensure the +// text stays on the baseline, we pass a multiplier to calculate a line-height in rems. +@function lineHeightFromBaseline($multiplier: 3) { + @return convertToRem(($euiFontSize / 2) * $multiplier); +} + +// Titles map +// Lists all the properties per EuiTitle size that then gets looped through to create the selectors. +// The map allows for tokenization and easier customization per theme, otherwise you'd have to override the selectors themselves +$euiTitles: ( + 'xxxs': ( + 'font-size': $euiFontSizeXS, + 'line-height': lineHeightFromBaseline(3), + 'font-weight': $euiFontWeightBold, + ), + 'xxs': ( + 'font-size': $euiFontSizeS, + 'line-height': lineHeightFromBaseline(3), + 'font-weight': $euiFontWeightBold, + ), + 'xs': ( + 'font-size': $euiFontSize, + 'line-height': lineHeightFromBaseline(3), + 'font-weight': $euiFontWeightSemiBold, + 'letter-spacing': -.02em, + ), + 's': ( + 'font-size': $euiFontSizeL, + 'line-height': lineHeightFromBaseline(4), + 'font-weight': $euiFontWeightMedium, + 'letter-spacing': -.025em, + ), + 'm': ( + 'font-size': $euiFontSizeXL, + 'line-height': lineHeightFromBaseline(5), + 'font-weight': $euiFontWeightLight, + 'letter-spacing': -.04em, + ), + 'l': ( + 'font-size': $euiFontSizeXXL, + 'line-height': lineHeightFromBaseline(6), + 'font-weight': $euiFontWeightLight, + 'letter-spacing': -.03em, + ), +) !default; diff --git a/packages/eui-theme-common/src/global_styling/variables/_z_index.scss b/packages/eui-theme-common/src/global_styling/variables/_z_index.scss new file mode 100644 index 00000000000..2448a34c61a --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/_z_index.scss @@ -0,0 +1,34 @@ +// Z-Index + +// Remember that z-index is relative to parent and based on the stacking context. +// z-indexes only compete against other z-indexes when they exist as children of +// that shared parent. + +// That means a popover with a settings of 2, will still show above a modal +// with a setting of 100, if it is within that modal and not besides it. + +// Generally that means it's a good idea to consider things added to this file +// as competitive only as siblings. + +// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context + +$euiZLevel0: 0; +$euiZLevel1: 1000; +$euiZLevel2: 2000; +$euiZLevel3: 3000; +$euiZLevel4: 4000; +$euiZLevel5: 5000; +$euiZLevel6: 6000; +$euiZLevel7: 7000; +$euiZLevel8: 8000; +$euiZLevel9: 9000; + +$euiZToastList: $euiZLevel9; +$euiZModal: $euiZLevel8; +$euiZMask: $euiZLevel6; +$euiZNavigation: $euiZLevel6; +$euiZContentMenu: $euiZLevel2; +$euiZHeader: $euiZLevel1; +$euiZFlyout: $euiZHeader; +$euiZMaskBelowHeader: $euiZHeader; +$euiZContent: $euiZLevel0; diff --git a/packages/eui-theme-common/src/global_styling/variables/animations.ts b/packages/eui-theme-common/src/global_styling/variables/animations.ts new file mode 100644 index 00000000000..46bce009c92 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/animations.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { CSSProperties } from 'react'; + +/** + * A constant storing the `prefers-reduced-motion` media query + * so that when it is turned off, animations are not run. + */ +export const euiCanAnimate = + '@media screen and (prefers-reduced-motion: no-preference)'; + +/** + * A constant storing the `prefers-reduced-motion` media query that will + * only apply the content if the setting is off (reduce). + */ +export const euiCantAnimate = + '@media screen and (prefers-reduced-motion: reduce)'; + +/** + * Speeds / Durations / Delays + */ + +export const EuiThemeAnimationSpeeds = [ + 'extraFast', + 'fast', + 'normal', + 'slow', + 'extraSlow', +] as const; + +export type _EuiThemeAnimationSpeed = (typeof EuiThemeAnimationSpeeds)[number]; + +export type _EuiThemeAnimationSpeeds = { + /** - Default value: 90ms */ + extraFast: CSSProperties['animationDuration']; + /** - Default value: 150ms */ + fast: CSSProperties['animationDuration']; + /** - Default value: 250ms */ + normal: CSSProperties['animationDuration']; + /** - Default value: 350ms */ + slow: CSSProperties['animationDuration']; + /** - Default value: 500ms */ + extraSlow: CSSProperties['animationDuration']; +}; + +/** + * Easings / Timing functions + */ + +export const EuiThemeAnimationEasings = ['bounce', 'resistance'] as const; + +export type _EuiThemeAnimationEasing = + (typeof EuiThemeAnimationEasings)[number]; + +export type _EuiThemeAnimationEasings = Record< + _EuiThemeAnimationEasing, + CSSProperties['animationTimingFunction'] +>; + +export type _EuiThemeAnimation = _EuiThemeAnimationEasings & + _EuiThemeAnimationSpeeds; diff --git a/packages/eui-theme-common/src/global_styling/variables/borders.ts b/packages/eui-theme-common/src/global_styling/variables/borders.ts new file mode 100644 index 00000000000..3673010aba2 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/borders.ts @@ -0,0 +1,74 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CSSProperties } from 'react'; +import { ColorModeSwitch } from '../types'; + +export interface _EuiThemeBorderWidthValues { + /** + * Thinnest width for border + * - Default value: 1px + */ + thin: CSSProperties['borderWidth']; + /** + * Thickest width for border + * - Default value: 2px + */ + thick: CSSProperties['borderWidth']; +} + +export interface _EuiThemeBorderRadiusValues { + /** + * Primary corner radius size + * - Default value: 6px + */ + medium: CSSProperties['borderRadius']; + /** + * Small corner radius size + * - Default value: 4px + */ + small: CSSProperties['borderRadius']; +} + +export interface _EuiThemeBorderColorValues { + /** + * Color for all borders; Default is `colors.lightShade` + */ + color: ColorModeSwitch; +} + +export interface _EuiThemeBorderValues extends _EuiThemeBorderColorValues { + /** + * Varied thicknesses for borders + */ + width: _EuiThemeBorderWidthValues; + /** + * Varied border radii + */ + radius: _EuiThemeBorderRadiusValues; +} + +export interface _EuiThemeBorderTypes { + /** + * Full `border` property string computed using `border.width.thin` and `border.color` + * - Default value: 1px solid [colors.lightShade] + */ + thin: CSSProperties['border']; + /** + * Full `border` property string computed using `border.width.thick` and `border.color` + * - Default value: 2px solid [colors.lightShade] + */ + thick: CSSProperties['border']; + /** + * Full editable style `border` property string computed using `border.width.thick` and `border.color` + * - Default value: 2px dotted [colors.lightShade] + */ + editable: CSSProperties['border']; +} + +export type _EuiThemeBorder = _EuiThemeBorderValues & _EuiThemeBorderTypes; diff --git a/packages/eui-theme-common/src/global_styling/variables/breakpoint.ts b/packages/eui-theme-common/src/global_styling/variables/breakpoint.ts new file mode 100644 index 00000000000..68aef1fba38 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/breakpoint.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const EuiThemeBreakpoints = ['xs', 's', 'm', 'l', 'xl'] as const; + +// This type cannot be a string enum / must be a generic string +// in case consumers add custom breakpoint sizes, such as xxl or xxs +export type _EuiThemeBreakpoint = string; + +// Explicitly list out each key so we can document default values +// via JSDoc (which is available to devs in IDE via intellisense) +export type _EuiThemeBreakpoints = Record<_EuiThemeBreakpoint, number> & { + /** - Default value: 0 */ + xs: number; + /** - Default value: 575 */ + s: number; + /** - Default value: 768 */ + m: number; + /** - Default value: 992 */ + l: number; + /** - Default value: 1200 */ + xl: number; +}; diff --git a/packages/eui-theme-common/src/global_styling/variables/buttons.ts b/packages/eui-theme-common/src/global_styling/variables/buttons.ts new file mode 100644 index 00000000000..41699ae9c16 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/buttons.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorModeSwitch, StrictColorModeSwitch } from '../types'; + +export type _EuiThemeButtonColors = { + backgroundPrimary: ColorModeSwitch; + backgroundAccent: ColorModeSwitch; + backgroundAccentSecondary: ColorModeSwitch; + backgroundSuccess: ColorModeSwitch; + backgroundWarning: ColorModeSwitch; + backgroundDanger: ColorModeSwitch; + backgroundText: ColorModeSwitch; + backgroundDisabled: ColorModeSwitch; + + backgroundFilledPrimary: ColorModeSwitch; + backgroundFilledAccent: ColorModeSwitch; + backgroundFilledAccentSecondary: ColorModeSwitch; + backgroundFilledSuccess: ColorModeSwitch; + backgroundFilledWarning: ColorModeSwitch; + backgroundFilledDanger: ColorModeSwitch; + backgroundFilledText: ColorModeSwitch; + backgroundFilledDisabled: ColorModeSwitch; + + textColorPrimary: ColorModeSwitch; + textColorAccent: ColorModeSwitch; + textColorAccentSecondary: ColorModeSwitch; + textColorSuccess: ColorModeSwitch; + textColorWarning: ColorModeSwitch; + textColorDanger: ColorModeSwitch; + textColorText: ColorModeSwitch; + textColorDisabled: ColorModeSwitch; + + textColorFilledPrimary: ColorModeSwitch; + textColorFilledAccent: ColorModeSwitch; + textColorFilledAccentSecondary: ColorModeSwitch; + textColorFilledSuccess: ColorModeSwitch; + textColorFilledWarning: ColorModeSwitch; + textColorFilledDanger: ColorModeSwitch; + textColorFilledText: ColorModeSwitch; + textColorFilledDisabled: ColorModeSwitch; +}; + +export type _EuiThemeButton = StrictColorModeSwitch<_EuiThemeButtonColors> & {}; diff --git a/packages/eui-theme-common/src/global_styling/variables/colors.ts b/packages/eui-theme-common/src/global_styling/variables/colors.ts new file mode 100644 index 00000000000..eb6834b8280 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/colors.ts @@ -0,0 +1,273 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ColorModeSwitch, StrictColorModeSwitch } from '../types'; + +/** + * Top 5 colors + */ +export type _EuiThemeBrandColors = { + /** + * Main brand color and used for most **call to actions** like buttons and links. + */ + primary: ColorModeSwitch; + /** + * Pulls attention to key indicators like **notifications** or number of selections. + */ + accent: ColorModeSwitch; + /** + * Secondary attention indicator with lower priority. + */ + accentSecondary: ColorModeSwitch; + /** + * Used for **positive** messages/graphics and additive actions. + */ + success: ColorModeSwitch; + /** + * Used for **warnings** and actions that have a potential to be destructive. + */ + warning: ColorModeSwitch; + /** + * Used for **negative** messages/graphics like errors and destructive actions. + */ + danger: ColorModeSwitch; +}; + +/** + * Every brand color must have a contrast computed text equivelant + */ +export type _EuiThemeBrandTextColors = { + /** + * Typically computed against `colors.primary` + * @deprecated - use `textPrimary` instead + */ + primaryText: ColorModeSwitch; + /** + * Typically computed against `colors.accent` + * @deprecated - use `textAccent` instead + */ + accentText: ColorModeSwitch; + /** + * Typically computed against `colors.success` + * @deprecated - use `textSuccess` instead + */ + successText: ColorModeSwitch; + /** + * Typically computed against `colors.warning` + * @deprecated - use `textWarning` instead + */ + warningText: ColorModeSwitch; + /** + * Typically computed against `colors.danger` + * @deprecated - use `textDanger` instead + */ + dangerText: ColorModeSwitch; + + textPrimary: ColorModeSwitch; + textAccent: ColorModeSwitch; + textAccentSecondary: ColorModeSwitch; + textSuccess: ColorModeSwitch; + textWarning: ColorModeSwitch; + textDanger: ColorModeSwitch; +}; + +export type _EuiThemeShadeColors = { + /** + * Used as the background color of primary **page content and panels** including modals and flyouts. + * @deprecated - use `white` + */ + emptyShade: ColorModeSwitch; + /** + * Used to lightly shade areas that contain **secondary content** or contain panel-like components. + * @deprecated - use specific semantic color tokens instead + */ + lightestShade: ColorModeSwitch; + /** + * Used for most **borders** and dividers (horizontal rules). + * @deprecated - use specific semantic color tokens instead + */ + lightShade: ColorModeSwitch; + /** + * The middle gray for all themes; this is the base for `colors.subdued`. + * @deprecated - use specific semantic color tokens instead + */ + mediumShade: ColorModeSwitch; + /** + * Slightly subtle graphic color + * @deprecated - use specific semantic color tokens instead + */ + darkShade: ColorModeSwitch; + /** + * Used as the **text** color and the background color for **inverted components** like tooltips and the control bar. + * @deprecated - use specific semantic color tokens instead + */ + darkestShade: ColorModeSwitch; + /** + * The opposite of `emptyShade` + * @deprecated - use`black` + */ + fullShade: ColorModeSwitch; +}; + +export type _EuiThemeTextColors = { + /** + * Computed against `colors.darkestShade` + * @deprecated - use `textParagraph` instead + */ + text: ColorModeSwitch; + /** + * Computed against `colors.text` + * @deprecated - use `textHeading` instead + */ + title: ColorModeSwitch; + /** + * Computed against `colors.mediumShade` + * @deprecated - use `textSubdued` instead + */ + subduedText: ColorModeSwitch; + /** + * Computed against `colors.primaryText` + */ + link: ColorModeSwitch; + + textParagraph: ColorModeSwitch; + textHeading: ColorModeSwitch; + textSubdued: ColorModeSwitch; + textDisabled: ColorModeSwitch; + textInverse: ColorModeSwitch; +}; + +export type _EuiThemeSpecialColors = { + /** + * The background color for the **whole window (body)** and is a computed value of `colors.lightestShade`. + * Provides denominator (background) value for **contrast calculations**. + * @deprecated - use backgroundPage instead + */ + body: ColorModeSwitch; + /** + * Used to **highlight text** when matching against search strings + */ + highlight: ColorModeSwitch; + /** + * Computed against `colors.darkestShade` + * @deprecated - use specific semantic tokens instead (e.g. backgroundDisabled, borderDisabled etc) + */ + disabled: ColorModeSwitch; + /** + * Computed against `colors.disabled` + * @deprecated - use textDisabled instead + */ + disabledText: ColorModeSwitch; + /** + * The base color for shadows that gets `transparentized` + * at a value based on the `colorMode` and then layered. + * @deprecated - use specific shadow tokens instead + */ + shadow: ColorModeSwitch; +}; + +export type _EuiThemeBackgroundColors = { + backgroundBasePrimary: ColorModeSwitch; + backgroundBaseAccent: ColorModeSwitch; + backgroundBaseAccentSecondary: ColorModeSwitch; + backgroundBaseSuccess: ColorModeSwitch; + backgroundBaseWarning: ColorModeSwitch; + backgroundBaseDanger: ColorModeSwitch; + backgroundBaseSubdued: ColorModeSwitch; + backgroundBaseDisabled: ColorModeSwitch; + backgroundBasePlain: ColorModeSwitch; + backgroundBasePage: ColorModeSwitch; + backgroundBaseHover: ColorModeSwitch; + backgroundBaseSelect: ColorModeSwitch; + backgroundBasePrepend: ColorModeSwitch; + backgroundBaseOverlay: ColorModeSwitch; + + backgroundLightPrimary: ColorModeSwitch; + backgroundLightAccent: ColorModeSwitch; + backgroundLightAccentSecondary: ColorModeSwitch; + backgroundLightSuccess: ColorModeSwitch; + backgroundLightWarning: ColorModeSwitch; + backgroundLightDanger: ColorModeSwitch; + backgroundLightText: ColorModeSwitch; + + backgroundFilledPrimary: ColorModeSwitch; + backgroundFilledAccent: ColorModeSwitch; + backgroundFilledAccentSecondary: ColorModeSwitch; + backgroundFilledSuccess: ColorModeSwitch; + backgroundFilledWarning: ColorModeSwitch; + backgroundFilledDanger: ColorModeSwitch; + backgroundFilledText: ColorModeSwitch; +}; + +/** TODO: remove once usages are re-mapped */ +export type _EuiThemeTransparentBackgroundColors = { + /** @deprecated */ + backgroundTransparent: string; + /** @deprecated */ + backgroundTransparentPrimary: ColorModeSwitch; + /** @deprecated */ + backgroundTransparentAccent: ColorModeSwitch; + /** @deprecated */ + backgroundTransparentAccentSecondary: ColorModeSwitch; + /** @deprecated */ + backgroundTransparentSuccess: ColorModeSwitch; + /** @deprecated */ + backgroundTransparentWarning: ColorModeSwitch; + /** @deprecated */ + backgroundTransparentDanger: ColorModeSwitch; + /** @deprecated */ + backgroundTransparentSubdued: ColorModeSwitch; + /** @deprecated */ + backgroundTransparentPlain: ColorModeSwitch; +}; + +export type _EuiThemeBorderColors = { + borderBasePrimary: ColorModeSwitch; + borderBaseAccent: ColorModeSwitch; + borderBaseAccentSecondary: ColorModeSwitch; + borderBaseSuccess: ColorModeSwitch; + borderBaseWarning: ColorModeSwitch; + borderBaseDanger: ColorModeSwitch; + borderBasePlain: ColorModeSwitch; + borderBaseSubdued: ColorModeSwitch; + borderBaseDisabled: ColorModeSwitch; + borderBaseFloating: ColorModeSwitch; +}; + +export type _EuiThemeConstantColors = { + /** + * Purest **white** + * @deprecated + */ + ghost: string; + /** + * Purest **black** + * @deprecated + */ + ink: string; + /** + * Purest **white** + */ + plainLight: string; + /** + * Purest **black** + */ + plainDark: string; +}; + +export type _EuiThemeColorsMode = _EuiThemeBrandColors & + _EuiThemeBrandTextColors & + _EuiThemeShadeColors & + _EuiThemeSpecialColors & + _EuiThemeTextColors & + _EuiThemeBackgroundColors & + _EuiThemeTransparentBackgroundColors & + _EuiThemeBorderColors; + +export type _EuiThemeColors = StrictColorModeSwitch<_EuiThemeColorsMode> & + _EuiThemeConstantColors; diff --git a/packages/eui-theme-common/src/global_styling/variables/components.ts b/packages/eui-theme-common/src/global_styling/variables/components.ts new file mode 100644 index 00000000000..ca36ddad5b7 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/components.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { StrictColorModeSwitch } from '../types'; +import { _EuiThemeButtonColors } from './buttons'; + +export type _EuiThemeComponents = { + buttons: StrictColorModeSwitch<_EuiThemeButtonColors>; +}; diff --git a/packages/eui-theme-common/src/global_styling/variables/index.ts b/packages/eui-theme-common/src/global_styling/variables/index.ts new file mode 100644 index 00000000000..36013c7eb09 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/index.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './animations'; +export * from './borders'; +export * from './breakpoint'; +export * from './colors'; +export * from './_colors_vis'; +export * from './levels'; +export * from './size'; +export * from './shadow'; +export * from './states'; +export * from './typography'; +export * from './buttons'; +export * from './components'; diff --git a/packages/eui-theme-common/src/global_styling/variables/levels.ts b/packages/eui-theme-common/src/global_styling/variables/levels.ts new file mode 100644 index 00000000000..7d38c71791b --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/levels.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CSSProperties } from 'react'; + +/** + * Z-Index + * + * Remember that z-index is relative to parent and based on the stacking context. + * z-indexes only compete against other z-indexes when they exist as children of + * that shared parent. + * + * That means a popover with a settings of 2, will still show above a modal + * with a setting of 100, if it is within that modal and not besides it. + * + * Generally that means it's a good idea to consider things added to this file + * as competitive only as siblings. + * + * https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context + */ + +export const EuiThemeLevels = [ + 'toast', + 'modal', + 'mask', + 'navigation', + 'menu', + 'header', + 'flyout', + 'maskBelowHeader', + 'content', +] as const; + +export type _EuiThemeLevel = (typeof EuiThemeLevels)[number]; + +export type _EuiThemeLevels = { + /** - Default value: 9000 */ + toast: NonNullable; + /** - Default value: 8000 */ + modal: NonNullable; + /** - Default value: 6000 */ + mask: NonNullable; + /** - Default value: 6000 */ + navigation: NonNullable; + /** - Default value: 2000 */ + menu: NonNullable; + /** - Default value: 1000 */ + header: NonNullable; + /** - Default value: 1000 */ + flyout: NonNullable; + /** - Default value: 1000 */ + maskBelowHeader: NonNullable; + /** - Default value: 0 */ + content: NonNullable; +}; diff --git a/packages/eui-theme-common/src/global_styling/variables/shadow.ts b/packages/eui-theme-common/src/global_styling/variables/shadow.ts new file mode 100644 index 00000000000..7761fbdb9a0 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/shadow.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const EuiThemeShadowSizes = ['xs', 's', 'm', 'l', 'xl'] as const; + +export type _EuiThemeShadowSize = (typeof EuiThemeShadowSizes)[number]; + +/** + * Shadow t-shirt sizes descriptions + */ +export const _EuiShadowSizesDescriptions: Record<_EuiThemeShadowSize, string> = + { + xs: 'Very subtle shadow used on small components.', + s: 'Adds subtle depth, usually used in conjunction with a border.', + m: 'Used on small sized portalled content like popovers.', + l: 'Primary shadow used in most cases to add visible depth.', + xl: 'Very large shadows used for large portalled style containers like modals and flyouts.', + }; + +export interface _EuiThemeShadowCustomColor { + color?: string; + property?: 'box-shadow' | 'filter'; +} diff --git a/packages/eui-theme-common/src/global_styling/variables/size.ts b/packages/eui-theme-common/src/global_styling/variables/size.ts new file mode 100644 index 00000000000..98428346b61 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/size.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type _EuiThemeBase = number; + +export const EuiThemeSizes = [ + 'xxs', + 'xs', + 's', + 'm', + 'base', + 'l', + 'xl', + 'xxl', + 'xxxl', + 'xxxxl', +] as const; + +export type _EuiThemeSize = (typeof EuiThemeSizes)[number]; + +export type _EuiThemeSizes = { + /** - Default value: 2px */ + xxs: string; + /** - Default value: 4px */ + xs: string; + /** - Default value: 8px */ + s: string; + /** - Default value: 12px */ + m: string; + /** - Default value: 16px */ + base: string; + /** - Default value: 24px */ + l: string; + /** - Default value: 32px */ + xl: string; + /** - Default value: 40px */ + xxl: string; + /** - Default value: 48px */ + xxxl: string; + /** - Default value: 64px */ + xxxxl: string; +}; diff --git a/packages/eui-theme-common/src/global_styling/variables/states.ts b/packages/eui-theme-common/src/global_styling/variables/states.ts new file mode 100644 index 00000000000..2d0283ac949 --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/states.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CSSProperties } from 'react'; +import { ColorModeSwitch } from '../types'; + +export interface _EuiThemeFocus { + /** + * Default color of the focus ring, some components may override this property + * - Default value: currentColor + */ + color: ColorModeSwitch; + /** + * Thickness of the outline + * - Default value: 2px + */ + width: CSSProperties['borderWidth']; + /** + * Used to transparentize the focus background color + * - Default value: { LIGHT: 0.1, DARK: 0.2 } + */ + transparency: ColorModeSwitch; + /** + * Default focus background color. Not all components set a background color on focus + * - Default value: `colors.primary` computed with `focus.transparency` + */ + backgroundColor: ColorModeSwitch; +} diff --git a/packages/eui-theme-common/src/global_styling/variables/typography.ts b/packages/eui-theme-common/src/global_styling/variables/typography.ts new file mode 100644 index 00000000000..80ebbc8b1cb --- /dev/null +++ b/packages/eui-theme-common/src/global_styling/variables/typography.ts @@ -0,0 +1,146 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CSSProperties } from 'react'; + +/** + * Font units of measure + */ + +export const EuiThemeFontUnits = ['rem', 'px', 'em'] as const; + +export type _EuiThemeFontUnit = (typeof EuiThemeFontUnits)[number]; + +/* + * Font scale + */ + +export const EuiThemeFontScales = [ + 'xxxs', + 'xxs', + 'xs', + 's', + 'm', + 'l', + 'xl', + 'xxl', +] as const; + +export type _EuiThemeFontScale = (typeof EuiThemeFontScales)[number]; + +export type _EuiThemeFontScales = Record<_EuiThemeFontScale, number>; + +/* + * Font base settings + */ + +export type _EuiThemeFontBase = { + /** + * The whole font family stack for all parts of the UI. + * We encourage only customizing the first font in the stack. + */ + family: string; + /** + * The font family used for monospace UI elements like EuiCode + */ + familyCode?: string; + /** + * The font family used for serif UI elements like blockquotes within EuiText + */ + familySerif?: string; + /** + * Controls advanced features OpenType fonts. + * https://developer.mozilla.org/en-US/docs/Web/CSS/font-feature-settings + */ + featureSettings?: string; + /** + * Sets the default units used for font size & line height set by UI components + * like EuiText or EuiTitle. Defaults to `rem`. + * + * NOTE: This may overridden by some internal usages, e.g. + * EuiText's `relative` size which must use `em`. + * + * @default 'rem' + */ + defaultUnits: _EuiThemeFontUnit; + /** + * A computed number that is 1/4 of `base` + */ + baseline: number; + /** + * Establishes the ideal line-height percentage, but it is the `baseline` integer that establishes the final pixel/rem value + */ + lineHeightMultiplier: number; +}; + +/* + * Font weights + */ + +export const EuiThemeFontWeights = [ + 'light', + 'regular', + 'medium', + 'semiBold', + 'bold', +] as const; + +export type _EuiThemeFontWeight = (typeof EuiThemeFontWeights)[number]; + +export type _EuiThemeFontWeights = { + /** - Default value: 300 */ + light: CSSProperties['fontWeight']; + /** - Default value: 400 */ + regular: CSSProperties['fontWeight']; + /** - Default value: 500 */ + medium: CSSProperties['fontWeight']; + /** - Default value: 600 */ + semiBold: CSSProperties['fontWeight']; + /** - Default value: 700 */ + bold: CSSProperties['fontWeight']; +}; + +/** + * Body / Base styles + */ + +export interface _EuiThemeBody { + /** + * A sizing key from one of the font scales to set as the base body font-size + */ + scale: _EuiThemeFontScale; + /** + * A font weight key for setting the base body weight + */ + weight: keyof _EuiThemeFontWeights; +} + +/** + * Title styles + */ + +export interface _EuiThemeTitle { + /** + * A font weight key for setting the base weight for titles and headings + */ + weight: keyof _EuiThemeFontWeights; +} + +/* + * Font + */ + +export type _EuiThemeFont = _EuiThemeFontBase & { + scale: _EuiThemeFontScales; + /** + * @see {@link https://eui.elastic.co/#/theming/typography/values%23font-weight | Reference} for more information + */ + weight: _EuiThemeFontWeights; + body: _EuiThemeBody; + title: _EuiThemeTitle; +}; diff --git a/packages/eui-theme-common/src/index.ts b/packages/eui-theme-common/src/index.ts new file mode 100644 index 00000000000..05e235c669b --- /dev/null +++ b/packages/eui-theme-common/src/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './global_styling'; + +export * from './types'; +export * from './utils'; diff --git a/packages/eui-theme-common/src/types.ts b/packages/eui-theme-common/src/types.ts new file mode 100644 index 00000000000..68c72d586df --- /dev/null +++ b/packages/eui-theme-common/src/types.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* TODO: duplicated types from /eui/src/components/common - extract to shared location */ + +/** + * Like `keyof typeof`, but for getting values instead of keys + * ValueOf + * Results in `'value1' | 'value2'` + */ +export type ValueOf = T[keyof T]; + +/** + * Replaces all properties on any type as optional, includes nested types + * + * @example + * ```ts + * interface Person { + * name: string; + * age?: number; + * spouse: Person; + * children: Person[]; + * } + * type PartialPerson = RecursivePartial; + * // results in + * interface PartialPerson { + * name?: string; + * age?: number; + * spouse?: RecursivePartial; + * children?: RecursivePartial[] + * } + * ``` + */ +export type RecursivePartial = { + [P in keyof T]?: T[P] extends NonAny[] // checks for nested any[] + ? T[P] + : T[P] extends readonly NonAny[] // checks for nested ReadonlyArray + ? T[P] + : T[P] extends Array + ? Array> + : T[P] extends ReadonlyArray + ? ReadonlyArray> + : T[P] extends Set // checks for Sets + ? Set> + : T[P] extends Map // checks for Maps + ? Map> + : T[P] extends NonAny // checks for primitive values + ? T[P] + : RecursivePartial; // recurse for all non-array and non-primitive values +}; +type NonAny = number | boolean | string | symbol | null; diff --git a/packages/eui/src/services/theme/utils.test.ts b/packages/eui-theme-common/src/utils.test.ts similarity index 96% rename from packages/eui/src/services/theme/utils.test.ts rename to packages/eui-theme-common/src/utils.test.ts index c654e4d3c22..26e5480756c 100644 --- a/packages/eui/src/services/theme/utils.test.ts +++ b/packages/eui-theme-common/src/utils.test.ts @@ -16,6 +16,7 @@ import { getComputed, buildTheme, mergeDeep, + getTokenName, } from './utils'; describe('isInverseColorMode', () => { @@ -277,3 +278,14 @@ describe('mergeDeep', () => { ).toEqual({ a: 1, b: { c: { d: 3, e: 5 } } }); }); }); + +describe('getTokenName', () => { + it('returns the correct token name', () => { + expect(getTokenName('backgroundBase', 'primary')).toEqual( + 'backgroundBasePrimary' + ); + expect(getTokenName('backgroundBase', 'primary', 'hovered')).toEqual( + 'backgroundPrimaryHovered' + ); + }); +}); diff --git a/packages/eui-theme-common/src/utils.ts b/packages/eui-theme-common/src/utils.ts new file mode 100644 index 00000000000..445e538b582 --- /dev/null +++ b/packages/eui-theme-common/src/utils.ts @@ -0,0 +1,409 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + EuiThemeColorMode, + EuiThemeColorModeInverse, + EuiThemeColorModeStandard, + EuiThemeModifications, + EuiThemeSystem, + EuiThemeShape, + EuiThemeComputed, + COLOR_MODES_STANDARD, + COLOR_MODES_INVERSE, +} from './global_styling'; + +export const DEFAULT_COLOR_MODE = COLOR_MODES_STANDARD.light; + +/** + * Returns whether the parameter is an object + * @param {any} obj - Anything + */ +const isObject = (obj: any) => obj && typeof obj === 'object'; + +/** + * Returns whether the provided color mode is `inverse` + * @param {string} colorMode - `light`, `dark`, or `inverse` + */ +export const isInverseColorMode = ( + colorMode?: string +): colorMode is EuiThemeColorModeInverse => { + return colorMode === COLOR_MODES_INVERSE; +}; + +/** + * Returns the color mode configured in the current EuiThemeProvider. + * Returns the parent color mode if none is explicity set. + * @param {string} colorMode - `light`, `dark`, or `inverse` + * @param {string} parentColorMode - `LIGHT` or `DARK`; used as the fallback + */ +export const getColorMode = ( + colorMode?: EuiThemeColorMode, + parentColorMode?: EuiThemeColorModeStandard +): EuiThemeColorModeStandard => { + if (colorMode == null) { + return parentColorMode || DEFAULT_COLOR_MODE; + } + const mode = colorMode.toUpperCase() as + | EuiThemeColorModeInverse + | EuiThemeColorModeStandard; + if (isInverseColorMode(mode)) { + return parentColorMode === COLOR_MODES_STANDARD.dark || + parentColorMode === undefined + ? COLOR_MODES_STANDARD.light + : COLOR_MODES_STANDARD.dark; + } else { + return mode; + } +}; + +/** + * Returns a value at a given path on an object. + * If `colorMode` is provided, will scope the value to the appropriate color mode key (LIGHT\DARK) + * @param {object} model - Object + * @param {string} _path - Dot-notated string to a path on the object + * @param {string} colorMode - `light` or `dark` + */ +export const getOn = ( + model: { [key: string]: any }, + _path: string, + colorMode?: EuiThemeColorModeStandard +) => { + const path = _path.split('.'); + let node = model; + while (path.length) { + const segment = path.shift()!; + + if (node.hasOwnProperty(segment) === false) { + if ( + colorMode && + node.hasOwnProperty(colorMode) === true && + node[colorMode].hasOwnProperty(segment) === true + ) { + if (node[colorMode][segment] instanceof Computed) { + node = node[colorMode][segment].getValue(null, {}, node, colorMode); + } else { + node = node[colorMode][segment]; + } + } else { + return undefined; + } + } else { + if (node[segment] instanceof Computed) { + node = node[segment].getValue(null, {}, node, colorMode); + } else { + node = node[segment]; + } + } + } + + return node; +}; + +/** + * Sets a value at a given path on an object. + * @param {object} model - Object + * @param {string} _path - Dot-notated string to a path on the object + * @param {any} string - The value to set + */ +export const setOn = ( + model: { [key: string]: any }, + _path: string, + value: any +) => { + const path = _path.split('.'); + const propertyName = path.pop()!; + let node = model; + + while (path.length) { + const segment = path.shift()!; + if (node.hasOwnProperty(segment) === false) { + node[segment] = {}; + } + node = node[segment]; + } + + node[propertyName] = value; + return true; +}; + +/** + * Creates a class to store the `computer` method and its eventual parameters. + * Allows for on-demand computation with up-to-date parameters via `getValue` method. + * @constructor + * @param {function} computer - Function to be computed + * @param {string | array} dependencies - Dependencies passed to the `computer` as parameters + */ +export class Computed { + constructor( + public computer: (...values: any[]) => T, + public dependencies: string | string[] = [] + ) {} + + /** + * Executes the `computer` method with the current state of the theme + * by taking into account previously computed values and modifications. + * @param {Proxy | object} base - Computed or uncomputed theme + * @param {Proxy | object} modifications - Theme value overrides + * @param {object} working - Partially computed theme + * @param {string} colorMode - `light` or `dark` + */ + getValue( + base: EuiThemeSystem | EuiThemeShape | null, + modifications: EuiThemeModifications = {}, + working: Partial, + colorMode?: EuiThemeColorModeStandard + ) { + if (!this.dependencies.length) { + return this.computer(working); + } + if (!Array.isArray(this.dependencies)) { + return this.computer( + getOn(working, this.dependencies) ?? + getOn(modifications, this.dependencies, colorMode) ?? + (base ? getOn(base, this.dependencies, colorMode) : working) + ); + } + return this.computer( + this.dependencies.map((dependency) => { + return ( + getOn(working, dependency) ?? + getOn(modifications, dependency, colorMode) ?? + (base ? getOn(base, dependency, colorMode) : working) + ); + }) + ); + } +} + +/** + * Returns a Class (`Computed`) that stores the arbitrary computer method + * and references to its optional dependecies. + * @param {function} computer - Arbitrary method to be called at compute time. + * @param {string | array} dependencies - Values that will be provided to `computer` at compute time. + */ +export function computed(computer: (value: EuiThemeComputed) => T): T; +export function computed( + computer: (value: any[]) => T, + dependencies: string[] +): T; +export function computed( + computer: (value: any) => T, + dependencies: string +): T; +export function computed( + comp: ((value: T) => T) | ((value: any) => T) | ((value: any[]) => T), + dep?: string | string[] +) { + return new Computed(comp, dep); +} + +/** + * Type guard to check for a Computed object based on object shape + * without relying on the Computed class directly + */ +const isComputedLike = (key: object): key is Computed => { + if (typeof key !== 'object' || Array.isArray(key)) return false; + + return key.hasOwnProperty('dependencies') && key.hasOwnProperty('computer'); +}; + +/** + * Takes an uncomputed theme, and computes and returns all values taking + * into consideration value overrides and configured color mode. + * Overrides take precedence, and only values in the current color mode + * are computed and returned. + * @param {Proxy} base - Object to transform into Proxy + * @param {Proxy | object} over - Unique identifier or name + * @param {string} colorMode - `light` or `dark` + */ +export const getComputed = ( + base: EuiThemeSystem, + over: Partial>, + colorMode: EuiThemeColorModeStandard +): EuiThemeComputed => { + const output: Partial = { themeName: base.key }; + + function loop( + base: { [key: string]: any }, + over: { [key: string]: any }, + checkExisting: boolean = false, + path?: string + ) { + Object.keys(base).forEach((key) => { + let newPath = path ? `${path}.${key}` : `${key}`; + // @ts-expect-error `key` is not necessarily a colorMode key + if ([...Object.values(COLOR_MODES_STANDARD), colorMode].includes(key)) { + if (key !== colorMode) { + return; + } else { + const colorModeSegment = new RegExp( + `(\\.${colorMode}\\b)|(\\b${colorMode}\\.)` + ); + newPath = newPath.replace(colorModeSegment, ''); + } + } + const existing = checkExisting && getOn(output, newPath); + if (!existing || isObject(existing)) { + // NOTE: the class type check for Computed is not true for themes created externally; + // we additionally check on the object shape to confirm a Computed value + const baseValue = + base[key] instanceof Computed || isComputedLike(base[key]) + ? base[key].getValue(base.root, over.root, output, colorMode) + : base[key]; + const overValue = + over[key] instanceof Computed || isComputedLike(over[key]) + ? over[key].getValue(base.root, over.root, output, colorMode) + : over[key]; + if (isObject(baseValue) && !Array.isArray(baseValue)) { + loop(baseValue, overValue ?? {}, checkExisting, newPath); + } else { + setOn(output, newPath, overValue ?? baseValue); + } + } + }); + } + // Compute standard theme values and apply overrides + loop(base, over); + // Compute and apply extension values only + loop(over, {}, true); + return output as EuiThemeComputed; +}; + +/** + * Builds a Proxy with a custom `handler` designed to self-reference values + * and prevent arbitrary value overrides. + * @param {object} model - Object to transform into Proxy + * @param {string} key - Unique identifier or name + */ +export const buildTheme = (model: T, key: string) => { + const handler: ProxyHandler> = { + getPrototypeOf(target) { + return Reflect.getPrototypeOf(target.model); + }, + + setPrototypeOf(target, prototype) { + return Reflect.setPrototypeOf(target.model, prototype); + }, + + isExtensible(target) { + return Reflect.isExtensible(target); + }, + + preventExtensions(target) { + return Reflect.preventExtensions(target.model); + }, + + getOwnPropertyDescriptor(target, key) { + return Reflect.getOwnPropertyDescriptor(target.model, key); + }, + + defineProperty(target, property, attributes) { + return Reflect.defineProperty(target.model, property, attributes); + }, + + has(target, property) { + return Reflect.has(target.model, property); + }, + + get(_target, property) { + if (property === 'key') { + return _target[property]; + } + + // prevent Safari from locking up when the proxy is used in dev tools + // as it doesn't support getPrototypeOf + if (property === '__proto__') return {}; + + const target = property === 'root' ? _target : _target.model || _target; + // @ts-ignore `string` index signature + const value = target[property]; + if (isObject(value) && !Array.isArray(value)) { + return new Proxy( + { + model: value, + root: _target.root, + key: `_${_target.key}`, + }, + handler + ); + } else { + return value; + } + }, + + set(target: any) { + return target; + }, + + deleteProperty(target: any) { + return target; + }, + + ownKeys(target) { + return Reflect.ownKeys(target.model); + }, + + apply(target: any) { + return target; + }, + + construct(target: any) { + return target; + }, + }; + const themeProxy = new Proxy({ model, root: model, key }, handler); + + return themeProxy; +}; + +/** + * Deeply merges two objects, using `source` values whenever possible. + * @param {object} _target - Object with fallback values + * @param {object} source - Object with desired values + */ +export const mergeDeep = ( + _target: { [key: string]: any }, + source: { [key: string]: any } = {} +) => { + const target = { ..._target }; + + if (!isObject(target) || !isObject(source)) { + return source; + } + + Object.keys(source).forEach((key) => { + const targetValue = target[key]; + const sourceValue = source[key]; + + if (isObject(targetValue) && isObject(sourceValue)) { + target[key] = mergeDeep({ ...targetValue }, { ...sourceValue }); + } else { + target[key] = sourceValue; + } + }); + + return target; +}; + +/** + * Returns token name string based on passed dynamic variants + * and additional prefix/suffix + */ +export const getTokenName = ( + prefix: string, + variant: string, + suffix?: string +) => { + const getCapitalized = (str: string) => + str.charAt(0).toUpperCase() + str.slice(1); + const colorName = variant.charAt(0).toUpperCase() + variant.slice(1); + const _suffix = suffix ? getCapitalized(suffix) : ''; + + return `${prefix}${getCapitalized(colorName)}${_suffix}`; +}; diff --git a/packages/eui-theme-common/tsconfig.cjs.json b/packages/eui-theme-common/tsconfig.cjs.json new file mode 100644 index 00000000000..6becea42efa --- /dev/null +++ b/packages/eui-theme-common/tsconfig.cjs.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib/cjs", + "target": "es6", + "module": "CommonJS", + "lib": [ + "es6", + "DOM" + ], + "moduleResolution": "Node", + "declaration": true, + "sourceMap": true, + "noEmitHelpers": true, + "incremental": true, + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "tsBuildInfoFile": "lib/cjs/.tsbuildinfo", + "importHelpers": false, + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules" + ], +} \ No newline at end of file diff --git a/packages/eui-theme-common/tsconfig.json b/packages/eui-theme-common/tsconfig.json new file mode 100644 index 00000000000..31450bf12d6 --- /dev/null +++ b/packages/eui-theme-common/tsconfig.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "rootDir": "src", + "outDir": "lib/esm", + "target": "ES2020", + "module": "ESNext", + "lib": [ + "ESNext", + "DOM" + ], + "moduleResolution": "Node", + "declaration": true, + "sourceMap": true, + "noEmitHelpers": true, + "incremental": true, + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "tsBuildInfoFile": "lib/esm/.tsbuildinfo" + }, + "include": [ + "src", + ], + "exclude": [ + "node_modules" + ], +} \ No newline at end of file diff --git a/packages/eui-theme-common/tsconfig.types.json b/packages/eui-theme-common/tsconfig.types.json new file mode 100644 index 00000000000..54e2031daae --- /dev/null +++ b/packages/eui-theme-common/tsconfig.types.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.cjs.json", + "compilerOptions": { + "outDir": "lib/cjs", + "declaration": true, + "declarationMap": true, + "isolatedModules": false, + "noEmit": false, + "allowJs": false, + "emitDeclarationOnly": true + }, + "exclude": ["node_modules", "**/*.test.ts"] +} \ No newline at end of file diff --git a/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Highlight.png b/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Highlight.png new file mode 100644 index 00000000000..5b07dd69337 Binary files /dev/null and b/packages/eui/.loki/reference/chrome_desktop_Editors_Syntax_EuiCodeBlock_Highlight.png differ diff --git a/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Highlight.png b/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Highlight.png new file mode 100644 index 00000000000..2af7856b04a Binary files /dev/null and b/packages/eui/.loki/reference/chrome_mobile_Editors_Syntax_EuiCodeBlock_Highlight.png differ diff --git a/packages/eui/.storybook/decorator.tsx b/packages/eui/.storybook/decorator.tsx index 6b78a2e2353..d30f8df311d 100644 --- a/packages/eui/.storybook/decorator.tsx +++ b/packages/eui/.storybook/decorator.tsx @@ -6,12 +6,37 @@ * Side Public License, v 1. */ -import React, { useState, useMemo, FunctionComponent } from 'react'; +import React, { + useState, + useMemo, + FunctionComponent, + useEffect, + useCallback, +} from 'react'; import { css } from '@emotion/react'; import type { Preview } from '@storybook/react'; +import { EuiThemeBorealis } from '@elastic/eui-theme-borealis'; import { EuiThemeColorMode } from '../src/services'; import { EuiProvider, EuiProviderProps } from '../src/components/provider'; +import { EuiThemeAmsterdam } from '../src/themes'; + +const EXPERIMENTAL_THEMES = [ + { + text: 'Borealis', + value: EuiThemeBorealis.key, + provider: EuiThemeBorealis, + }, +]; + +export const AVAILABLE_THEMES = [ + { + text: 'Amsterdam', + value: EuiThemeAmsterdam.key, + provider: EuiThemeAmsterdam, + }, + ...EXPERIMENTAL_THEMES, +]; /** * Primary EuiProvider decorator to wrap around all stories @@ -20,8 +45,9 @@ import { EuiProvider, EuiProviderProps } from '../src/components/provider'; export const EuiProviderDecorator: FunctionComponent< EuiProviderProps<{}> & { writingMode: WritingModes; + themeName: string; } -> = ({ children, writingMode, ...euiProviderProps }) => { +> = ({ children, writingMode, themeName, theme, ...euiProviderProps }) => { // Append portals into Storybook's root div (rather than ) // so that loki correctly captures them for VRT screenshots const [sibling, setPortalSibling] = useState(null); @@ -39,8 +65,28 @@ export const EuiProviderDecorator: FunctionComponent< [writingMode] ); + const getTheme = useCallback(() => { + return AVAILABLE_THEMES.find((t) => themeName?.includes(t.value)); + }, [themeName]); + + const [_theme, setTheme] = useState(getTheme); + + useEffect(() => { + if (!themeName || theme) return; + + setTheme(getTheme); + }, [themeName, theme, getTheme]); + + const euiThemeProp = { + theme: theme ?? _theme?.provider, + }; + return ( - +
{portalInsert && children}
@@ -122,3 +168,18 @@ export const euiProviderDecoratorGlobals: Preview['globalTypes'] = { }, }, }; + +export const euiProviderDecoratorGlobalsExperimental = { + theme: { + description: 'Theme for EuiProvider', + defaultValue: EuiThemeAmsterdam.key, + toolbar: { + title: 'Theme', + items: [ + { value: EuiThemeAmsterdam.key, title: 'Amsterdam', icon: 'box' }, + { value: EuiThemeBorealis.key, title: 'Borealis', icon: 'box' }, + ], + dynamicTitle: true, + }, + }, +}; diff --git a/packages/eui/.storybook/preview.tsx b/packages/eui/.storybook/preview.tsx index c34435f5032..adf24f7f409 100644 --- a/packages/eui/.storybook/preview.tsx +++ b/packages/eui/.storybook/preview.tsx @@ -37,8 +37,13 @@ setEuiDevProviderWarning('error'); /** * Custom global decorators */ +import { isExperimentalThemeEnabled } from '../src/themes'; import { customJsxDecorator } from './addons/code-snippet/decorators/jsx_decorator'; -import { EuiProviderDecorator, euiProviderDecoratorGlobals } from './decorator'; +import { + EuiProviderDecorator, + euiProviderDecoratorGlobals, + euiProviderDecoratorGlobalsExperimental, +} from './decorator'; const preview: Preview = { decorators: [ @@ -48,12 +53,18 @@ const preview: Preview = { colorMode={context.globals.colorMode} {...(context.componentId === 'theming-euiprovider' && context.args)} writingMode={context.globals.writingMode} + themeName={context.globals.theme} > ), ], - globalTypes: { ...euiProviderDecoratorGlobals }, + globalTypes: isExperimentalThemeEnabled() + ? { + ...euiProviderDecoratorGlobals, + ...euiProviderDecoratorGlobalsExperimental, + } + : { ...euiProviderDecoratorGlobals }, parameters: { backgrounds: { disable: true }, // Use colorMode instead options: { diff --git a/packages/eui/package.json b/packages/eui/package.json index 08e65f012c9..218404751a6 100644 --- a/packages/eui/package.json +++ b/packages/eui/package.json @@ -16,8 +16,9 @@ ], "scripts": { "start": "cross-env BABEL_MODULES=false webpack serve --config=src-docs/webpack.config.js", + "build:workspaces": "yarn workspaces foreach -Rti --from @elastic/eui-theme-common run build && yarn workspaces foreach -Rti --from @elastic/eui --exclude @elastic/eui --exclude @elastic/eui-theme-common run build", "build-docs": "cross-env BABEL_MODULES=false cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=4096 webpack --config=src-docs/webpack.config.js", - "build": "yarn extract-i18n-strings && node ./scripts/compile-clean.js && node ./scripts/compile-eui.js && yarn compile-scss", + "build": "node ./scripts/compile-i18n-strings.js && node ./scripts/compile-clean.js && node ./scripts/compile-eui.js && yarn compile-scss", "build-pack": "yarn build && npm pack", "compile-icons": "node ./scripts/compile-icons.js && prettier --write --loglevel=warn \"./src/components/icon/assets/**/*.tsx\"", "compile-scss": "node ./scripts/compile-scss.js", @@ -30,7 +31,7 @@ "test": "yarn lint && yarn test-unit", "test-ci": "yarn test && yarn test-cypress", "test-unit": "node ./scripts/test-unit", - "test-staged": "yarn lint && node scripts/test-staged.js", + "test-staged": "yarn build:workspaces && yarn lint && node scripts/test-staged.js", "test-cypress": "node ./scripts/test-cypress", "test-cypress-dev": "yarn test-cypress --dev", "test-cypress-a11y": "yarn test-cypress --a11y", @@ -103,6 +104,8 @@ "@cypress/webpack-dev-server": "^1.7.0", "@elastic/charts": "^64.1.0", "@elastic/datemath": "^5.0.3", + "@elastic/eui-theme-borealis": "workspace:^", + "@elastic/eui-theme-common": "workspace:^", "@emotion/babel-preset-css-prop": "^11.11.0", "@emotion/cache": "^11.11.0", "@emotion/css": "^11.11.0", @@ -253,6 +256,8 @@ }, "peerDependencies": { "@elastic/datemath": "^5.0.2", + "@elastic/eui-theme-borealis": "0.0.1", + "@elastic/eui-theme-common": "0.0.1", "@emotion/css": "11.x", "@emotion/react": "11.x", "@types/react": "^16.9 || ^17.0 || ^18.0", diff --git a/packages/eui/scripts/compile-i18n-strings.js b/packages/eui/scripts/compile-i18n-strings.js new file mode 100644 index 00000000000..f948577d2fd --- /dev/null +++ b/packages/eui/scripts/compile-i18n-strings.js @@ -0,0 +1,26 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +const { execSync } = require('child_process'); +const chalk = require('chalk'); +const yargs = require('yargs/yargs'); +const { hideBin } = require('yargs/helpers'); + +const { NODE_ENV } = process.env; + +const info = chalk.white; +const log = chalk.grey; + +if (NODE_ENV !== 'production') return; + +const command = 'yarn extract-i18n-strings'; + +console.log(log(command)); +execSync(command, { + stdio: 'inherit', +}); diff --git a/packages/eui/scripts/deploy/build_docs b/packages/eui/scripts/deploy/build_docs index ab1207b3337..8e700a97b0b 100755 --- a/packages/eui/scripts/deploy/build_docs +++ b/packages/eui/scripts/deploy/build_docs @@ -12,4 +12,4 @@ docker run \ --volume "$PWD":/app \ --workdir /app \ "$DOCKER_BASE_IMAGE" \ - bash -c 'yarn && yarn --cwd packages/eui build && yarn --cwd packages/eui build-docs && yarn --cwd packages/eui build-storybook' + bash -c 'yarn && yarn --cwd packages/eui build:workspaces && yarn --cwd packages/eui build && yarn --cwd packages/eui build-docs && yarn --cwd packages/eui build-storybook' diff --git a/packages/eui/src-docs/src/components/guide_theme_selector/guide_theme_selector.tsx b/packages/eui/src-docs/src/components/guide_theme_selector/guide_theme_selector.tsx index bc9d4449b9d..0f99e738533 100644 --- a/packages/eui/src-docs/src/components/guide_theme_selector/guide_theme_selector.tsx +++ b/packages/eui/src-docs/src/components/guide_theme_selector/guide_theme_selector.tsx @@ -6,7 +6,8 @@ import { useEuiTheme, useIsWithinBreakpoints, } from '../../../../src/services'; -import { EUI_THEME, EUI_THEMES } from '../../../../src/themes'; +import { EUI_THEME } from '../../../../src/themes'; +import { AVAILABLE_THEMES } from '../with_theme/theme_context'; import { ThemeContext } from '../with_theme'; // @ts-ignore Not TS @@ -51,15 +52,15 @@ const GuideThemeSelectorComponent: React.FunctionComponent< const systemColorMode = useEuiTheme().colorMode.toLowerCase(); const currentTheme: EUI_THEME = - EUI_THEMES.find( + AVAILABLE_THEMES.find( (theme) => theme.value === (context.theme ?? systemColorMode) - ) || EUI_THEMES[0]; + ) || AVAILABLE_THEMES[0]; const getIconType = (value: EUI_THEME['value']) => { return value === currentTheme.value ? 'check' : 'empty'; }; - const items = EUI_THEMES.map((theme) => { + const items = AVAILABLE_THEMES.map((theme) => { return ( value); +const THEME_NAMES = AVAILABLE_THEMES.map(({ value }) => value); const THEME_LANGS = theme_languages.map(({ id }) => id); type ThemeContextType = { diff --git a/packages/eui/src-docs/src/index.js b/packages/eui/src-docs/src/index.js index a69a07e3613..d9b072fccf4 100644 --- a/packages/eui/src-docs/src/index.js +++ b/packages/eui/src-docs/src/index.js @@ -4,6 +4,8 @@ import { Provider } from 'react-redux'; import { HashRouter, Switch, Route, Redirect } from 'react-router-dom'; import { Helmet } from 'react-helmet'; +import { isExperimentalThemeEnabled } from '../../src/themes'; + import configureStore from './store/configure_store'; import { AppContext } from './views/app_context'; @@ -13,13 +15,25 @@ import { NotFoundView } from './views/not_found/not_found_view'; import { registerTheme, ExampleContext } from './services'; import Routes from './routes'; +import { + ThemeProvider, + AVAILABLE_THEMES, +} from './components/with_theme/theme_context'; + +// TODO: update SCSS files for new theme once available import themeLight from './theme_light.scss'; import themeDark from './theme_dark.scss'; -import { ThemeProvider } from './components/with_theme/theme_context'; +import themeNewLight from './theme_new_light.scss'; +import themeNewDark from './theme_new_dark.scss'; registerTheme('light', [themeLight]); registerTheme('dark', [themeDark]); +if (isExperimentalThemeEnabled()) { + registerTheme(AVAILABLE_THEMES[2].value, [themeNewLight]); + registerTheme(AVAILABLE_THEMES[3].value, [themeNewDark]); +} + // Set up app // Whether the docs app should be wrapped in diff --git a/packages/eui/src-docs/src/theme_new_dark.scss b/packages/eui/src-docs/src/theme_new_dark.scss new file mode 100644 index 00000000000..7c76c518a04 --- /dev/null +++ b/packages/eui/src-docs/src/theme_new_dark.scss @@ -0,0 +1,7 @@ +@import 'node_modules/@elastic/eui-theme-borealis/src/theme_dark'; +@import './components/index'; +@import './services/playground/index'; +@import './views/index'; + +// Elastic charts +@import '~@elastic/charts/dist/theme'; diff --git a/packages/eui/src-docs/src/theme_new_light.scss b/packages/eui/src-docs/src/theme_new_light.scss new file mode 100644 index 00000000000..1bee3ff7412 --- /dev/null +++ b/packages/eui/src-docs/src/theme_new_light.scss @@ -0,0 +1,7 @@ +@import 'node_modules/@elastic/eui-theme-borealis/src/theme_light'; +@import './components/index'; +@import './services/playground/index'; +@import './views/index'; + +// Elastic charts +@import '~@elastic/charts/dist/theme'; diff --git a/packages/eui/src-docs/src/views/app_context.js b/packages/eui/src-docs/src/views/app_context.js index 729f4c94079..42645f5b2f1 100644 --- a/packages/eui/src-docs/src/views/app_context.js +++ b/packages/eui/src-docs/src/views/app_context.js @@ -11,7 +11,7 @@ import { setEuiDevProviderWarning, euiStylisPrefixer, } from '../../../src/services'; -import { EUI_THEMES } from '../../../src/themes'; +import { AVAILABLE_THEMES } from '../components/with_theme/theme_context'; import favicon16Prod from '../images/favicon/prod/favicon-16x16.png'; import favicon32Prod from '../images/favicon/prod/favicon-32x32.png'; @@ -55,9 +55,13 @@ export const AppContext = ({ children }) => { default: generalEmotionCache, utility: utilityCache, }} - theme={EUI_THEMES.find((t) => t.value === theme)?.provider} + theme={AVAILABLE_THEMES.find((t) => t.value === theme)?.provider} colorMode={ - theme ? (theme.includes('light') ? 'light' : 'dark') : undefined + theme + ? theme.toLowerCase().includes('light') + ? 'light' + : 'dark' + : undefined } > diff --git a/packages/eui/src-docs/src/views/theme/color/_color_js.tsx b/packages/eui/src-docs/src/views/theme/color/_color_js.tsx index b4749bb0575..41fa0bd5edc 100644 --- a/packages/eui/src-docs/src/views/theme/color/_color_js.tsx +++ b/packages/eui/src-docs/src/views/theme/color/_color_js.tsx @@ -9,17 +9,13 @@ import { EuiCode, EuiColorPickerSwatch, EuiText, - useEuiBackgroundColor, - useEuiPaddingSize, BACKGROUND_COLORS, - euiBackgroundColor, - useEuiPaddingCSS, EuiButtonGroup, EuiDescribedFormGroup, EuiPanel, EuiSpacer, - _EuiBackgroundColorOptions, logicalCSS, + useEuiPaddingCSS, } from '../../../../../src'; import { EuiThemeColors, ThemeRowType } from '../_props'; @@ -33,6 +29,11 @@ import { text_colors, } from '../../../../../src/themes/amsterdam/global_styling/variables/_colors'; import { ThemeValuesTable } from '../_components/_theme_values_table'; +import { + _EuiThemeBackgroundColors, + _EuiThemeTransparentBackgroundColors, + getTokenName, +} from '@elastic/eui-theme-common'; export const brandKeys = Object.keys(brand_colors); @@ -265,6 +266,8 @@ export const SpecialValuesJS = () => { }; export const UtilsJS = () => { + const { euiTheme } = useEuiTheme(); + return ( <> @@ -298,7 +301,9 @@ export const UtilsJS = () => { } example={

- background-color: {useEuiBackgroundColor('accent')} + + background-color: {euiTheme.colors.backgroundBaseAccent} +

} snippetLanguage="tsx" @@ -309,38 +314,12 @@ const cssStyles = [colorStyles['accent']]; /* Your content */ `} /> - - useEuiBackgroundColor(color, method?)} - type="hook" - props={`color: '${BACKGROUND_COLORS.join("' | '")}'; - -method? 'opaque' | 'transparent';`} - description={ -

- Returns just the computed background color for the given{' '} - color. -

- } - example={ -

- {useEuiBackgroundColor('subdued')} -

- } - snippetLanguage="emotion" - snippet={"background: ${useEuiBackgroundColor('subdued')};"} - /> ); }; export const UtilsValuesJS = () => { - const euiTheme = useEuiTheme(); + const { euiTheme } = useEuiTheme(); const backgroundButtons = ['opaque', 'transparent'].map((m) => { return { id: m, @@ -381,16 +360,21 @@ export const UtilsValuesJS = () => { { + const backgroundToken = getTokenName('backgroundBase', color); + const transparentBackgroundToken = + color === 'transparent' + ? backgroundToken + : getTokenName('backgroundTransparent', color); + + const token = + backgroundSelected === 'transparent' + ? (transparentBackgroundToken as keyof _EuiThemeTransparentBackgroundColors) + : (backgroundToken as keyof _EuiThemeBackgroundColors); + return { id: color, - token: - backgroundSelected === 'transparent' - ? `useEuiBackgroundColor('${color}', 'transparent')` - : `useEuiBackgroundColor('${color}')`, - value: euiBackgroundColor(euiTheme, color, { - method: - backgroundSelected as _EuiBackgroundColorOptions['method'], - }), + token, + value: euiTheme.colors[token], }; })} render={(item) => ( diff --git a/packages/eui/src-docs/src/views/theme/color/_contrast_js.tsx b/packages/eui/src-docs/src/views/theme/color/_contrast_js.tsx index ca98bc014b7..b675c444eb3 100644 --- a/packages/eui/src-docs/src/views/theme/color/_contrast_js.tsx +++ b/packages/eui/src-docs/src/views/theme/color/_contrast_js.tsx @@ -27,6 +27,7 @@ type ColorSection = { showTextVariants: boolean; matchPanelColor?: boolean; hookName?: string; + tokenName?: string; }; export const ColorSectionJS: FunctionComponent = ({ @@ -36,6 +37,7 @@ export const ColorSectionJS: FunctionComponent = ({ showTextVariants, matchPanelColor, hookName, + tokenName, }) => { const { euiTheme } = useEuiTheme(); const colorsForContrast = showTextVariants ? textVariants : allowedColors; @@ -80,7 +82,9 @@ export const ColorSectionJS: FunctionComponent = ({ background={_colorValue ? colorValue : color} key={color2} minimumContrast={minimumContrast} - styleString={hookName && `${hookName}('${color}')`} + styleString={ + tokenName ?? (hookName && `${hookName}('${color}')`) + } /> ); })} diff --git a/packages/eui/src-docs/src/views/theme/color/contrast.tsx b/packages/eui/src-docs/src/views/theme/color/contrast.tsx index e9a96a4c286..e724687c8e3 100644 --- a/packages/eui/src-docs/src/views/theme/color/contrast.tsx +++ b/packages/eui/src-docs/src/views/theme/color/contrast.tsx @@ -25,17 +25,17 @@ import { brandKeys, shadeKeys } from './_color_js'; import { ContrastSlider } from './_contrast_slider'; import { ratingAA } from './_contrast_utilities'; import { _EuiThemeColorsMode } from '../../../../../src/global_styling/variables/colors'; -import { - BACKGROUND_COLORS, - _EuiBackgroundColor, - euiBackgroundColor, -} from '../../../../../src/global_styling'; +import { BACKGROUND_COLORS } from '../../../../../src/global_styling'; import { BUTTON_COLORS, euiButtonColor, _EuiButtonColor, -} from '../../../../../src/themes/amsterdam/global_styling/mixins/button'; +} from '../../../../../src/global_styling/mixins/_button'; import { GuideSection } from '../../../components/guide_section/guide_section'; +import { + _EuiThemeBackgroundColors, + getTokenName, +} from '@elastic/eui-theme-common'; // This array is used inside routes.js to create the sidenav sub-sections export const contrastSections = [ @@ -66,9 +66,7 @@ export default () => { const [backgroundColors, setBackgroundColors] = useState(background_colors); - const [backgroundFunction, setBackgroundFunction] = useState( - 'useEuiBackgroundColor' - ); + const [backgroundFunction, setBackgroundFunction] = useState(undefined); const [backgroundSelected, setBackgroundSelected] = useState( backgroundButtons[0].id ); @@ -78,12 +76,7 @@ export default () => { case 'container': setBackgroundSelected(id); setBackgroundColors(background_colors); - setBackgroundFunction('useEuiBackgroundColor(color)'); - break; - case 'hover': - setBackgroundSelected(id); - setBackgroundColors(background_colors); - setBackgroundFunction("useEuiBackgroundColor(color, 'transparent')"); + setBackgroundFunction(undefined); break; case 'button': setBackgroundSelected(id); @@ -269,8 +262,13 @@ export default () => { description={

These background colors are pre-defined shades of the - brand colors. They are recalled by using the hook{' '} - {backgroundFunction}. + brand colors.{' '} + {backgroundFunction && ( + <> + They are recalled by using the hook{' '} + {backgroundFunction} + + )}

} > @@ -289,6 +287,11 @@ export default () => { {backgroundColors.map((color: string) => { + const backgroundToken = getTokenName( + 'backgroundBase', + color + ) as keyof _EuiThemeBackgroundColors; + switch (backgroundSelected) { case 'container': return ( @@ -296,34 +299,10 @@ export default () => { - - - ); - - case 'hover': - return ( - - diff --git a/packages/eui/src-docs/src/views/theme/sizing/_sizing_js.tsx b/packages/eui/src-docs/src/views/theme/sizing/_sizing_js.tsx index f263e09be50..f3a65b6b607 100644 --- a/packages/eui/src-docs/src/views/theme/sizing/_sizing_js.tsx +++ b/packages/eui/src-docs/src/views/theme/sizing/_sizing_js.tsx @@ -12,7 +12,6 @@ import { logicalShorthandCSS, EuiText, useEuiPaddingSize, - useEuiBackgroundColor, useEuiBackgroundColorCSS, useEuiPaddingCSS, EuiAccordion, @@ -363,6 +362,8 @@ export const UtilsJS = () => { }; export const PaddingJS = () => { + const { euiTheme } = useEuiTheme(); + return ( <> @@ -425,7 +426,7 @@ const cssStyles = [paddingStyles['l']]; example={

diff --git a/packages/eui/src/components/badge/color_utils.ts b/packages/eui/src/components/badge/color_utils.ts index 351404bce19..6d3a923e8ec 100644 --- a/packages/eui/src/components/badge/color_utils.ts +++ b/packages/eui/src/components/badge/color_utils.ts @@ -12,7 +12,7 @@ import { UseEuiTheme, isColorDark, tint } from '../../services'; import { euiButtonColor, euiButtonFillColor, -} from '../../themes/amsterdam/global_styling/mixins'; +} from '../../global_styling/mixins/_button'; import { chromaValid, parseColor } from '../color_picker/utils'; export const euiBadgeColors = (euiThemeContext: UseEuiTheme) => { diff --git a/packages/eui/src/components/breadcrumbs/_breadcrumb_content.styles.ts b/packages/eui/src/components/breadcrumbs/_breadcrumb_content.styles.ts index be36e19dbaf..d85f1c92c0b 100644 --- a/packages/eui/src/components/breadcrumbs/_breadcrumb_content.styles.ts +++ b/packages/eui/src/components/breadcrumbs/_breadcrumb_content.styles.ts @@ -17,7 +17,7 @@ import { logicalBorderRadiusCSS, mathWithUnits, } from '../../global_styling'; -import { euiButtonColor } from '../../themes/amsterdam/global_styling/mixins/button'; +import { euiButtonColor } from '../../global_styling/mixins/_button'; /** * Styles cast to inner ,