diff --git a/package.json b/package.json index fc016a62ca6..e59e5745a9b 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,7 @@ "@elastic/datemath": "^5.0.3", "@elastic/eslint-config-kibana": "^0.15.0", "@emotion/babel-preset-css-prop": "^11.0.0", + "@emotion/cache": "^11.4.0", "@emotion/eslint-plugin": "^11.0.0", "@emotion/jest": "^11.1.0", "@emotion/react": "^11.1.1", diff --git a/src-docs/src/components/guide_page/index.js b/src-docs/src/components/guide_page/index.js index b401f0fe0e1..9d0d6aed863 100644 --- a/src-docs/src/components/guide_page/index.js +++ b/src-docs/src/components/guide_page/index.js @@ -1,3 +1,5 @@ export { GuidePage } from './guide_page'; export { GuidePageChrome } from './guide_page_chrome'; + +export { GuidePageHeader } from './guide_page_header'; diff --git a/src-docs/src/components/index.js b/src-docs/src/components/index.js index f202a572b56..ab6b007380f 100644 --- a/src-docs/src/components/index.js +++ b/src-docs/src/components/index.js @@ -7,7 +7,7 @@ export { export { GuideMarkdownFormat } from './guide_markdown_format'; -export { GuidePage, GuidePageChrome } from './guide_page'; +export { GuidePage, GuidePageChrome, GuidePageHeader } from './guide_page'; export { GuideSectionContainer as GuideSection } from './guide_section/guide_section_container'; diff --git a/src-docs/src/components/with_theme/theme_context.tsx b/src-docs/src/components/with_theme/theme_context.tsx index de73faa2021..67a3cd74901 100644 --- a/src-docs/src/components/with_theme/theme_context.tsx +++ b/src-docs/src/components/with_theme/theme_context.tsx @@ -2,12 +2,11 @@ import React from 'react'; import { EUI_THEMES, EUI_THEME } from '../../../../src/themes'; // @ts-ignore importing from a JS file import { applyTheme } from '../../services'; -import { EuiThemeProvider } from '../../../../src/services'; const THEME_NAMES = EUI_THEMES.map(({ value }) => value); const defaultState = { - theme: THEME_NAMES[0], + theme: THEME_NAMES[2], changeTheme: (themeValue: EUI_THEME['value']) => { applyTheme(themeValue); }, @@ -49,12 +48,7 @@ export class ThemeProvider extends React.Component { changeTheme: this.changeTheme, }} > - t.value === theme)?.provider} - colorMode={theme.includes('light') ? 'light' : 'dark'} - > - {children} - + {children} ); } diff --git a/src-docs/src/index.html b/src-docs/src/index.html index 6a8eba081c2..6823472c33e 100644 --- a/src-docs/src/index.html +++ b/src-docs/src/index.html @@ -14,6 +14,7 @@ +
diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index b417d22f592..15dc533c469 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -170,6 +170,8 @@ import { PortalExample } from './views/portal/portal_example'; import { ProgressExample } from './views/progress/progress_example'; +import { ProviderExample } from './views/provider/provider_example'; + import { RangeControlExample } from './views/range/range_example'; import { TreeViewExample } from './views/tree_view/tree_view_example'; @@ -476,6 +478,7 @@ const navigation = [ OverlayMaskExample, PortalExample, PrettyDurationExample, + ProviderExample, ResizeObserverExample, ResponsiveExample, TextDiffExample, diff --git a/src-docs/src/views/app_view.js b/src-docs/src/views/app_view.js index cf33804176c..35f3c2eaa70 100644 --- a/src-docs/src/views/app_view.js +++ b/src-docs/src/views/app_view.js @@ -1,7 +1,8 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { Helmet } from 'react-helmet'; -import { GuidePageChrome, ThemeContext } from '../components'; +import createCache from '@emotion/cache'; +import { GuidePageChrome, ThemeContext, GuidePageHeader } from '../components'; import { translateUsingPseudoLocale } from '../services'; import { @@ -9,10 +10,12 @@ import { EuiPage, EuiContext, EuiPageBody, + EuiProvider, } from '../../../src/components'; +import { EUI_THEMES } from '../../../src/themes'; + import { keys } from '../../../src/services'; -import { GuidePageHeader } from '../components/guide_page/guide_page_header'; import favicon16Prod from '../images/favicon/prod/favicon-16x16.png'; import favicon32Prod from '../images/favicon/prod/favicon-32x32.png'; @@ -21,6 +24,11 @@ import favicon16Dev from '../images/favicon/dev/favicon-16x16.png'; import favicon32Dev from '../images/favicon/dev/favicon-32x32.png'; import favicon96Dev from '../images/favicon/dev/favicon-96x96.png'; +const emotionCache = createCache({ + key: 'eui-docs', + container: document.querySelector('#emotion-global-insert'), +}); + export class AppView extends Component { componentDidUpdate(prevProps) { if (prevProps.currentRoute.path !== this.props.currentRoute.path) { @@ -36,7 +44,7 @@ export class AppView extends Component { document.removeEventListener('keydown', this.onKeydown); } - renderContent() { + render() { const { children, currentRoute, toggleLocale, locale, routes } = this.props; const { navigation } = routes; @@ -53,63 +61,63 @@ export class AppView extends Component { const isLocalDev = window.location.host.includes('803'); return ( - <> - - {`${this.props.currentRoute.name} - Elastic UI Framework`} - - - - - - - - - - - + + {({ theme }) => ( + t.value === theme)?.provider} + colorMode={theme.includes('light') ? 'light' : 'dark'} + > + + {`${this.props.currentRoute.name} - Elastic UI Framework`} + + + + - - {(context) => { - return React.cloneElement(children, { - selectedTheme: context.theme, + + + + + + + + {React.cloneElement(children, { + selectedTheme: theme, title: currentRoute.name, - }); - }} - + })} + + - - - + + )} + ); } - render() { - return this.renderContent(); - } - onKeydown = (event) => { if (event.target !== document.body) { return; diff --git a/src-docs/src/views/guidelines/getting_started.md b/src-docs/src/views/guidelines/getting_started.md index 4e758d334fd..b200c2d0098 100644 --- a/src-docs/src/views/guidelines/getting_started.md +++ b/src-docs/src/views/guidelines/getting_started.md @@ -88,6 +88,20 @@ import { findTestSubject } from '@elastic/eui/lib/test'; You can consume EUI in standalone projects, such as plugins and prototypes. +### Using the global provider + +For EUI to work correctly, set up `EuiProvider` at the root of your application. + +```js +import { EuiProvider } from '@elastic/eui' + +const App = ({ Component }) => ( + + + +); +``` + ### Importing compiled CSS Most of the time, you just need the compiled CSS, which provides the styling for the React components. @@ -96,17 +110,11 @@ Most of the time, you just need the compiled CSS, which provides the styling for import '@elastic/eui/dist/eui_theme_light.css'; ``` -Other compiled themes include: +For the dark theme, you can import + ```js import '@elastic/eui/dist/eui_theme_dark.css'; ``` -```js -import '@elastic/eui/dist/eui_theme_amsterdam_light.css'; -``` -```js -import '@elastic/eui/dist/eui_theme_amsterdam_dark.css'; -``` - ### Using our Sass variables on top of compiled CSS If you want to build **on top** of the EUI theme by accessing the Sass variables, functions, and mixins, you'll need to import the Sass globals in addition to the compiled CSS mentioned above. This will require `style`, `css`, `postcss`, and `sass` loaders. @@ -114,22 +122,15 @@ If you want to build **on top** of the EUI theme by accessing the Sass variables First import the correct colors file, followed by the globals file. ```scss -@import '@elastic/eui/src/themes/eui/eui_colors_light.scss'; -@import '@elastic/eui/src/themes/eui/eui_globals.scss'; +@import '@elastic/eui/src/themes/amsterdam/_colors_light.scss'; +@import '@elastic/eui/src/themes/amsterdam/_globals.scss'; ``` For the dark theme, swap the first import for the dark colors file. ```scss -@import '@elastic/eui/src/themes/eui/eui_colors_dark.scss'; -@import '@elastic/eui/src/themes/eui/eui_globals.scss'; -``` - -If you want to use the new, but in progress Amsterdam theme, you can import it similarly. - -```scss -@import '@elastic/eui/src/themes/eui-amsterdam/eui_amsterdam_colors_light.scss'; -@import '@elastic/eui/src/themes/eui-amsterdam/eui_amsterdam_globals.scss'; +@import '@elastic/eui/src/themes/amsterdam/_colors_dark.scss'; +@import '@elastic/eui/src/themes/amsterdam/_globals.scss'; ``` ### Using Sass to customize EUI @@ -144,21 +145,13 @@ Here is an example setup. // mytheme.scss $euiColorPrimary: #7B61FF; -@import '@elastic/eui/src/themes/eui/eui_colors_light.scss'; -@import '@elastic/eui/src/themes/eui/eui_globals.scss'; +@import '@elastic/eui/src/themes/amsterdam/theme_light.scss'; ``` ### Fonts By default, EUI ships with a font stack that includes some outside, open source fonts. If your system is internet available you can include these by adding the following imports to your SCSS/CSS files, otherwise you'll need to bundle the physical fonts in your build. EUI will drop to System Fonts (which you may prefer) in their absence. -```scss -@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:400,400i,700,700i'); -@import url('https://rsms.me/inter/inter-ui.css'); -``` - -The Amsterdam theme uses the latest version of Inter that can be grabbed from Google Fonts as well. - ```scss @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap'); ``` diff --git a/src-docs/src/views/provider/provider.md b/src-docs/src/views/provider/provider.md new file mode 100644 index 00000000000..264678fd265 --- /dev/null +++ b/src-docs/src/views/provider/provider.md @@ -0,0 +1,114 @@ +## EuiProvider + +`EuiProvider` contains all necessary context providers required for full functionality of EUI, including reset and globals styles, and `EuiThemeProvider` for theming and writing custom styles. + + +### Basic setup + +For EUI to work correctly, set up `EuiProvider` at the root of your application. + +```jsx +import { EuiProvider } from '@elastic/eui' + +const App = ({ Component }) => ( + + + +); +``` + +See [**EuiThemeProvider**](/#/theming/theme-provider) for full documentation as all relevant props will pass through. For instance, it's likely that you will want to implement color mode switching at this level: + +```jsx + +``` + + +### Configuration + +#### Global styles + +A reset stylesheet and global EUI styles will by default be applied via CSS-in-JS. To prevent loading these styles from loading, pass `theme={null}` to the provider. + + +#### `@emotion/cache` and style injection location + +In the case that your app has its own static stylesheet, the global styles may not be injected into the correct location in the ``, causing unintentional overrides or unapplied styles. [The **@emotion/cache** library](https://emotion.sh/docs/@emotion/cache) provides configuration options that help with specifying the injection location. + +```html + + + + + My App + + + +
+ + +``` + +```jsx +// App.js +import createCache from '@emotion/cache'; +import { EuiProvider } from '@elastic/eui' + +const cache = createCache({ + key: 'myApp', + container: document.querySelector('#global-style-insert'), +}); + +const App = () => ( + + {/* Content */} + +); +``` + +Any other options available with [the **createCache** API](https://emotion.sh/docs/@emotion/cache#createcache) will be respected by EUI. +Note that EUI does not include the `@emotion/cache` library, so you will need to add it to your application dependencies. + + +### Recommended app structure + +Although it is possible to recreate the functionality of `EuiProvider` by composing its constituant parts, this is not recommended. More context, functionality, and configuration will be added to `EuiProvider` in future releases. Nested instances of `EuiThemeProvider`, however, are entirely valid. + +```jsx +// App.js +import * as React from 'react'; +import createCache from '@emotion/cache'; +import { EuiProvider } from '@elastic/eui' + +import Component from './Component'; + +// If necessary: +const cache = createCache({ + key: 'myApp', + container: document.querySelector('#global-style-insert'), +}); + +const App = () => { + const [isDarkMode, setIsDarkMode] = React.useState(false); + + return ( + + + + ) +}; +``` + +```jsx +// Component.js +import { EuiThemeProvider } from '@elastic/eui'; + +const Component = () => ( +
+ {/* Content using the global color mode */} + + {/* Content using the inverse of the global color mode */} + +
+); +``` diff --git a/src-docs/src/views/provider/provider_example.js b/src-docs/src/views/provider/provider_example.js new file mode 100644 index 00000000000..ab4418c8724 --- /dev/null +++ b/src-docs/src/views/provider/provider_example.js @@ -0,0 +1,14 @@ +import React from 'react'; + +import { EuiMarkdownFormat } from '../../../../src/components'; + +const providerSource = require('!!raw-loader!./provider.md').default; + +export const ProviderExample = { + title: 'Provider', + sections: [ + { + text: {providerSource}, + }, + ], +}; diff --git a/src-docs/src/views/theme/consuming.tsx b/src-docs/src/views/theme/consuming.tsx index 31916fec0df..046ff07cfc8 100644 --- a/src-docs/src/views/theme/consuming.tsx +++ b/src-docs/src/views/theme/consuming.tsx @@ -1,9 +1,5 @@ import React from 'react'; -import { EuiSpacer } from '../../../../src/components/spacer'; -import { EuiIcon } from '../../../../src/components/icon'; -import { EuiCode } from '../../../../src/components/code'; -import { EuiText } from '../../../../src/components/text'; -import { useEuiTheme } from '../../../../src/services'; +import { EuiIcon, EuiCode, EuiText, useEuiTheme } from '../../../../src'; export default () => { const { euiTheme } = useEuiTheme(); @@ -17,19 +13,17 @@ export default () => { />{' '} This primary color will adjust based on the light or dark theme value

- -
-

- The padding of this box is created using calc(){' '} - because EUI's theme sizes are string pixel values that are - calculated off the theme's base -

-
+ The padding of this box is created using calc(){' '} + because EUI's theme sizes are string pixel values that are + calculated off the theme's base +

); }; diff --git a/src-docs/src/views/theme/provider.tsx b/src-docs/src/views/theme/provider.tsx new file mode 100644 index 00000000000..9454943d271 --- /dev/null +++ b/src-docs/src/views/theme/provider.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { + EuiIcon, + EuiCode, + EuiText, + useEuiTheme, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, +} from '../../../../src'; + +export default () => { + const { euiTheme, colorMode } = useEuiTheme(); + return ( + + + +
+
+
+ + + {euiTheme.themeName} + + + + + colorMode: + + + + +

+ {colorMode} +

+
+
+
+
+ ); +}; diff --git a/src-docs/src/views/theme/theme_example.js b/src-docs/src/views/theme/theme_example.js index f1f98e8b92f..01a94a216f0 100644 --- a/src-docs/src/views/theme/theme_example.js +++ b/src-docs/src/views/theme/theme_example.js @@ -2,15 +2,11 @@ import React from 'react'; import { GuideSectionTypes } from '../../components'; -import { - EuiText, - EuiSpacer, - EuiCallOut, - EuiCode, - EuiLink, -} from '../../../../src/components'; +import { EuiText, EuiCode, EuiLink } from '../../../../src/components'; import { EuiThemeProvider } from '../../../../src/services'; +import Provider from './provider'; + import Consuming from './consuming'; const consumingSource = require('!!raw-loader!./consuming'); @@ -38,13 +34,15 @@ export const ThemeExample = {

EUI is in the progress of switching it's core styles processor - from Sass to Emotion. It - requires that all consumer applications wrap their core application - with EuiThemeProvider. + from Sass to Emotion. To + take full advantage of this context layer, wrap the root of your + application with{' '} + + EuiProvider + + .

- - ), sections: [ @@ -60,9 +58,15 @@ export const ThemeExample = {

Typically your app will only need a single instance at the top level - and the functionality will flow down the component tree. It is also - possible to use several nested theme providers. In this case each - nested provider will inherit from its closest ancestor provider. + and the functionality will flow down the component tree. We + recommend using{' '} + + EuiProvider + {' '} + at this level as it includes reset styles and future configuration + options. It is also possible to use several nested theme providers. + In this case each nested provider will inherit from its closest + ancestor provider.

EuiThemeProvider accepts three props, all of @@ -91,6 +95,7 @@ export const ThemeExample = {

), + demo: , props: { EuiThemeProvider }, }, { diff --git a/src/components/index.ts b/src/components/index.ts index c74dbbbf706..2ef0e25fab2 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -127,6 +127,8 @@ export * from './portal'; export * from './progress'; +export * from './provider'; + export * from './tree_view'; export * from './observer/resize_observer'; diff --git a/src/components/provider/index.ts b/src/components/provider/index.ts new file mode 100644 index 00000000000..ea3ec0b586b --- /dev/null +++ b/src/components/provider/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 { EuiProvider, EuiProviderProps } from './provider'; diff --git a/src/components/provider/provider.tsx b/src/components/provider/provider.tsx new file mode 100644 index 00000000000..d8a1fbe657c --- /dev/null +++ b/src/components/provider/provider.tsx @@ -0,0 +1,55 @@ +/* + * 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 React, { PropsWithChildren } from 'react'; +import { CacheProvider, EmotionCache } from '@emotion/react'; + +import { + EuiGlobalStyles, + EuiGlobalStylesProps, +} from '../../global_styling/reset/global_styles'; +import { + EuiThemeProvider, + EuiThemeProviderProps, + EuiThemeSystem, +} from '../../services'; +import { EuiThemeAmsterdam } from '../../themes/amsterdam/theme'; + +export interface EuiProviderProps + extends Omit, 'children' | 'theme'>, + EuiGlobalStylesProps { + /** + * Provide a specific EuiTheme; Defaults to EuiThemeDefault; + * Pass `null` to remove all theming including global reset + */ + theme?: EuiThemeSystem | null; + cache?: EmotionCache; +} + +export function EuiProvider({ + cache, + theme = EuiThemeAmsterdam, + colorMode, + modify, + children, +}: PropsWithChildren>) { + return theme !== null ? ( + + {cache ? ( + + + + ) : ( + + )} + {children} + + ) : ( + {children} + ); +} diff --git a/src/global_styling/index.scss b/src/global_styling/index.scss index e480e5adb92..5c29e0435a5 100644 --- a/src/global_styling/index.scss +++ b/src/global_styling/index.scss @@ -12,8 +12,7 @@ // Utility classes provide one-off selectors for common css problems @import 'utility/index'; -// The reset file makes use of variables and mixins -@import 'reset/index'; +// The reset file has moved to global_styles.tsx // Customization of the React Date Picker @import 'react_date_picker/index'; diff --git a/src/global_styling/mixins/_helpers.ts b/src/global_styling/mixins/_helpers.ts index 83138f69804..2a9ffb9bb36 100644 --- a/src/global_styling/mixins/_helpers.ts +++ b/src/global_styling/mixins/_helpers.ts @@ -10,9 +10,74 @@ import chroma from 'chroma-js'; import { useEuiTheme } from '../../services/theme/hooks'; import { transparentize } from '../../services/color'; import { useOverflowShadow } from './_shadow'; +import { CSSProperties } from 'react'; /** - * NOTE: These were quick conversions of their Sass counterparts. + * Set scroll bar appearance on Chrome (and firefox). + * All parameters are optional and default to specific global settings. + */ +export const useScrollBar = ({ + thumbColor: _thumbColor, + trackColor = 'transparent', + width = 'thin', + size: _size, + corner: _corner, +}: { + thumbColor?: CSSProperties['backgroundColor']; + trackColor?: CSSProperties['backgroundColor']; + /** + * Defaults to `thin`. Use `auto` only for large page scrollbars + */ + width?: CSSProperties['scrollbarWidth']; + /** + * Overall width (height for horizontal scrollbars) + */ + size?: CSSProperties['width']; + /** + * Corner sizes are usually determined by `width` and + * are used as an inset border and therefore a smaller corner size means a larger thumb + */ + corner?: CSSProperties['borderWidth']; +} = {}) => { + const { + euiTheme: { colors, size }, + } = useEuiTheme(); + + // Set defaults from theme + const thumbColor = _thumbColor || transparentize(colors.darkShade, 0.5); + const scrollBarSize = _size || size.base; + const scrollBarCorner = + _corner || width === 'thin' ? `calc(${size.s} * 0.75)` : size.xs; + + // 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 + const firefoxSupport = `scrollbar-color: ${thumbColor} ${trackColor};`; + + return `scrollbar-width: ${width}; + + &::-webkit-scrollbar { + width: ${scrollBarSize}; + height: ${scrollBarSize}; + } + + &::-webkit-scrollbar-thumb { + background-color: ${thumbColor}; + background-clip: content-box; + border-radius: ${scrollBarSize}; + border: ${scrollBarCorner} solid ${trackColor}; + } + + &::-webkit-scrollbar-corner, + &::-webkit-scrollbar-track { + background-color: ${trackColor}; + } + + ${firefoxSupport} + `; +}; + +/** + * NOTE: The ones below this comment were quick conversions of their Sass counterparts. * They have yet to be used/tested. */ @@ -52,39 +117,6 @@ export const useInnerBorder = ({ `; }; -// Set scroll bar appearance on Chrome (and firefox). -export const useScrollBar = ({ - thumbColor: _thumbColor, - trackBackgroundColor: _trackBackgroundColor, -}: { - thumbColor?: string; - trackBackgroundColor?: string; -} = {}) => { - const { - euiTheme: { colors, size }, - } = useEuiTheme(); - const thumbColor = _thumbColor || colors.darkShade; - const trackBackgroundColor = _trackBackgroundColor || 'transparent'; - // 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 - return ` - scrollbar-width: thin; - &::-webkit-scrollbar { - width: ${size.base}; - height: ${size.base}; - } - &::-webkit-scrollbar-thumb { - background-color: ${transparentize(thumbColor, 0.5)}; - border: calc(${size.base} * 0.75) solid ${trackBackgroundColor}; - background-clip: content-box; - } - &::-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. diff --git a/src/global_styling/mixins/_typography.ts b/src/global_styling/mixins/_typography.ts new file mode 100644 index 00000000000..04585ad0444 --- /dev/null +++ b/src/global_styling/mixins/_typography.ts @@ -0,0 +1,13 @@ +/* + * 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 { useEuiTheme, isLegacyTheme } from '../../services/theme'; + +// Some functions 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. diff --git a/src/global_styling/reset/_hacks.scss b/src/global_styling/reset/_hacks.scss deleted file mode 100644 index 3a69b4bcd26..00000000000 --- a/src/global_styling/reset/_hacks.scss +++ /dev/null @@ -1,5 +0,0 @@ -// Chrome has an issue around RTL languages in SVGs when letter-spacing is negative -// https://bugs.chromium.org/p/chromium/issues/detail?id=966480 -svg text { - letter-spacing: normal !important; // sass-lint:disable-line no-important -} \ No newline at end of file diff --git a/src/global_styling/reset/global_styles.tsx b/src/global_styling/reset/global_styles.tsx new file mode 100644 index 00000000000..d2f7cc0c6d5 --- /dev/null +++ b/src/global_styling/reset/global_styles.tsx @@ -0,0 +1,138 @@ +/* + * 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 React from 'react'; +import { Global, css } from '@emotion/react'; +import { useScrollBar } from '../mixins/_helpers'; +import { shade, tint, transparentize } from '../../services/color'; +import { useEuiTheme } from '../../services/theme'; +import { resetStyles as reset } from './reset'; + +export interface EuiGlobalStylesProps {} + +export const EuiGlobalStyles = ({}: EuiGlobalStylesProps) => { + const { + euiTheme: { base, border, colors, font }, + colorMode, + } = useEuiTheme(); + + /** + * Declaring the top level scrollbar colors to match the theme also requires setting the sizes on Chrome + * so that it knows to use custom styles. Therefore, we just reuse the same scrollbar mixin with thick size. + */ + const scrollbarStyles = useScrollBar({ + trackColor: + colorMode === 'LIGHT' + ? shade(colors.body, 0.03) + : tint(colors.body, 0.07), + width: 'auto', + }); + + /** + * This font reset sets all our base font/typography related properties + * that are needed to override browser-specific element settings. + */ + const fontReset = ` + font-family: ${font.family}; + font-size: ${`${font.scale[font.body.scale] * base}px`}; + line-height: ${base / (font.scale[font.body.scale] * base)}; + font-weight: ${font.weight[font.body.weight]}; + ${ + font.body.letterSpacing + ? `letter-spacing: ${font.body.letterSpacing};` + : '' + } + `; + + /** + * Outline/Focus state resets + */ + const focusReset = () => { + // The latest theme utilizes `focus-visible` to turn on focus outlines. + // But this is browser-dependend: + // 👉 Safari and Firefox innately respect only showing the outline with keyboard only + // 💔 But they don't allow coloring of the 'auto'/default outline, so contrast is no good in dark mode. + // 👉 For these browsers we use the solid type in order to match with \`currentColor\`. + // 😦 Which does means the outline will be square + return `*:focus { + outline: currentColor solid ${border.width.thick}; + outline-offset: calc(-(${border.width.thick} / 2) * -1); + + // 👀 Chrome respects :focus-visible and allows coloring the \`auto\` style + &:focus-visible { + outline-style: auto; + } + + //🙅‍♀️ But Chrome also needs to have the outline forcefully removed from regular \`:focus\` state + &:not(:focus-visible) { + outline: none; + } + } + + // Dark mode's highlighted doesn't work well so lets just set it the same as our focus background + ::selection { + background: ${transparentize( + colors.primary, + colorMode === 'LIGHT' ? 0.1 : 0.2 + )} + }`; + }; + + /** + * Final styles + */ + const styles = css` + ${reset} + + html { + ${scrollbarStyles} + ${fontReset} + text-size-adjust: 100%; + font-kerning: normal; + height: 100%; + background-color: ${colors.body}; + color: ${colors.text}; + } + + code, + pre, + kbd, + samp { + font-family: ${font.familyCode}; + } + + input, + textarea, + select, + button { + ${fontReset} + } + + em { + font-style: italic; + } + + strong { + font-weight: ${font.weight.bold}; + } + + ${focusReset()} + + a { + color: ${colors.primaryText}; + + &, + &:hover, + &:focus { + text-decoration: none; + } + } + `; + + return ; +}; diff --git a/src/global_styling/reset/reset.ts b/src/global_styling/reset/reset.ts new file mode 100644 index 00000000000..085a35ef231 --- /dev/null +++ b/src/global_styling/reset/reset.ts @@ -0,0 +1,107 @@ +/* + * 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 resetStyles = ` +/* // Adapted from Eric Meyer's reset (http://meyerweb.com/eric/tools/css/reset/, v2.0 | 20110126). */ + + +*, *:before, *:after { + box-sizing: border-box; +} + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: none; + vertical-align: baseline; +} + +h1, h2, h3, h4, h5, h6, p { + font-family: inherit; + font-weight: inherit; + font-size: inherit; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +a[href], +button, +[role='button'] { + cursor: pointer; +} + +button { + background: none; + border: none; + padding: 0; + margin: 0; + color: inherit; + border-radius: 0; +} + +input { + margin: 0; + padding: 0; +} + +input:disabled { + opacity: 1; /* required on iOS */ +} + +ol, +ul { + list-style: none; +} + +blockquote, +q { + quotes: none; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +hr { + margin: 0; +} + +fieldset { + min-inline-size: auto; +} + +/* Chrome has an issue around RTL languages in SVGs when letter-spacing is negative + * https://bugs.chromium.org/p/chromium/issues/detail?id=966480 + */ +svg text { + letter-spacing: normal !important; +}`; diff --git a/src/global_styling/variables/_typography.ts b/src/global_styling/variables/_typography.ts index e344fe8202b..4527627b0df 100644 --- a/src/global_styling/variables/_typography.ts +++ b/src/global_styling/variables/_typography.ts @@ -96,10 +96,20 @@ export const fontWeight: _EuiThemeFontWeight = { export type EuiThemeFont = _EuiThemeFontBase & { scale: { [key in _EuiThemeFontScale]: number }; weight: _EuiThemeFontWeight; + body: { + scale: _EuiThemeFontScale; + weight: keyof _EuiThemeFontWeight; + letterSpacing?: CSSProperties['letterSpacing']; + }; }; export const font: EuiThemeFont = { ...fontBase, scale: fontScale, weight: fontWeight, + body: { + scale: 'm', + weight: 'regular', + letterSpacing: '-.005em', + }, }; diff --git a/src/services/index.ts b/src/services/index.ts index a0d5677f501..5a903fc6baf 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -142,6 +142,7 @@ export { withEuiTheme, WithEuiThemeProps, EuiThemeProvider, + EuiThemeProviderProps, buildTheme, computed, isInverseColorMode, diff --git a/src/services/theme/index.ts b/src/services/theme/index.ts index c62d3ad6565..a50979aa75d 100644 --- a/src/services/theme/index.ts +++ b/src/services/theme/index.ts @@ -13,7 +13,7 @@ export { EuiColorModeContext, } from './context'; export { useEuiTheme, withEuiTheme, WithEuiThemeProps } from './hooks'; -export { EuiThemeProvider } from './provider'; +export { EuiThemeProvider, EuiThemeProviderProps } from './provider'; export { buildTheme, computed, diff --git a/src/themes/amsterdam/global_styling/index.scss b/src/themes/amsterdam/global_styling/index.scss index 36432e23208..cd7d60a3cca 100644 --- a/src/themes/amsterdam/global_styling/index.scss +++ b/src/themes/amsterdam/global_styling/index.scss @@ -12,8 +12,7 @@ // Utility classes provide one-off selectors for common css problems @import '../../../global_styling/utility/index'; -// The reset file makes use of variables and mixins -@import 'reset/index'; +// The reset file has moved to global_styles.tsx // Customization of the React Date Picker @import 'react_date_picker/index'; diff --git a/src/themes/amsterdam/global_styling/reset/_index.scss b/src/themes/amsterdam/global_styling/reset/_index.scss deleted file mode 100644 index 03302a02c0f..00000000000 --- a/src/themes/amsterdam/global_styling/reset/_index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'reset'; -@import '../../../../global_styling/reset/hacks'; -@import '../../../../global_styling/reset/scrollbar'; diff --git a/src/themes/amsterdam/global_styling/reset/_reset.scss b/src/themes/amsterdam/global_styling/reset/_reset.scss deleted file mode 100644 index 6b03436f64c..00000000000 --- a/src/themes/amsterdam/global_styling/reset/_reset.scss +++ /dev/null @@ -1,158 +0,0 @@ -// This file allows multi-line selectors to make it more readable -// sass-lint:disable single-line-per-selector - -/** - * Adapted from Eric Meyer's reset (http://meyerweb.com/eric/tools/css/reset/, v2.0 | 20110126). - * - */ - -*, *:before, *:after { - box-sizing: border-box; -} - -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: none; - vertical-align: baseline; -} - -code, pre, kbd, samp { - font-family: $euiCodeFontFamily; -} - -h1, h2, h3, h4, h5, h6, p { - font-family: inherit; - font-weight: inherit; - font-size: inherit; -} - -input, textarea, select, button { - font-family: $euiFontFamily; -} - -em { - font-style: italic; -} - -strong { - font-weight: $euiFontWeightBold; -} - -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} - -html { - @include euiFont; - font-size: $euiFontSize; - color: $euiTextColor; - height: 100%; - background-color: $euiPageBackgroundColor; -} - -body { - line-height: $euiBodyLineHeight; -} - -*:focus { - // 👉 Safari and Firefox innately respect only showing the outline with keyboard only - // 💔 But they don't allow coloring of the 'auto'/default outline, so contrast is no good in dark mode. - // 👉 For these browsers we use the solid type in order to match with `currentColor`. - // 😦 Which does means the outline will be square - outline: $euiFocusRingSize solid currentColor; - outline-offset: -($euiFocusRingSize / 2); - - // 👀 Chrome respects :focus-visible and allows coloring the `auto` style - &:focus-visible { - outline-style: auto; - } - - //🙅‍♀️ But Chrome also needs to have the outline forcefully removed from regular `:focus` state - &:not(:focus-visible) { - outline: none; - } -} - -// Dark mode's highlighted doesn't work well so lets just set it the same as our focus background -::selection { - background: $euiFocusBackgroundColor; -} - -a { - text-decoration: none; - color: $euiColorPrimary; - - &:hover, - &:focus { - text-decoration: none; - } -} - -a:hover, button, [role='button'] { - cursor: pointer; -} - -input { - margin: 0; - padding: 0; - - &:disabled { - opacity: 1; /* required on iOS */ - } -} - -button { - background: none; - border: none; - padding: 0; - margin: 0; - font-size: inherit; - color: inherit; - border-radius: 0; - - &:hover { - cursor: pointer; - } -} - -ol, ul { - list-style: none; -} - -blockquote, q { - quotes: none; -} - -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; // sass-lint:disable-line no-duplicate-properties -} - -table { - border-collapse: collapse; - border-spacing: 0; -} - -hr { - margin: 0; -} - -fieldset { - min-inline-size: auto; -} diff --git a/src/themes/amsterdam/global_styling/variables/_typography.ts b/src/themes/amsterdam/global_styling/variables/_typography.ts index c854b7546c3..8959e440693 100644 --- a/src/themes/amsterdam/global_styling/variables/_typography.ts +++ b/src/themes/amsterdam/global_styling/variables/_typography.ts @@ -17,4 +17,8 @@ import { export const font_ams: EuiThemeFont = { ...font, family: "'Inter', BlinkMacSystemFont, Helvetica, Arial, sans-serif", + body: { + scale: 's', + weight: 'regular', + }, }; diff --git a/src/themes/legacy/legacy_dark.scss b/src/themes/legacy/legacy_dark.scss index 5494ea78a68..29a24f349f9 100644 --- a/src/themes/legacy/legacy_dark.scss +++ b/src/themes/legacy/legacy_dark.scss @@ -4,5 +4,8 @@ // Global styling @import '../../global_styling/index'; +// Global reset +@import './reset/index'; + // Components @import '../../components/index'; diff --git a/src/themes/legacy/legacy_light.scss b/src/themes/legacy/legacy_light.scss index cc83a57ef99..85778023067 100644 --- a/src/themes/legacy/legacy_light.scss +++ b/src/themes/legacy/legacy_light.scss @@ -4,5 +4,8 @@ // Global styling @import '../../global_styling/index'; +// Global reset +@import './reset/index'; + // Components @import '../../components/index'; diff --git a/src/global_styling/reset/_index.scss b/src/themes/legacy/reset/_index.scss similarity index 69% rename from src/global_styling/reset/_index.scss rename to src/themes/legacy/reset/_index.scss index 23aead08d55..4a15b73c70c 100644 --- a/src/global_styling/reset/_index.scss +++ b/src/themes/legacy/reset/_index.scss @@ -1,3 +1,2 @@ @import 'reset'; -@import 'hacks'; @import 'scrollbar'; diff --git a/src/global_styling/reset/_reset.scss b/src/themes/legacy/reset/_reset.scss similarity index 91% rename from src/global_styling/reset/_reset.scss rename to src/themes/legacy/reset/_reset.scss index e4f8104bef2..5af84b30e98 100644 --- a/src/global_styling/reset/_reset.scss +++ b/src/themes/legacy/reset/_reset.scss @@ -151,3 +151,10 @@ hr { fieldset { min-inline-size: auto; } + +/* Chrome has an issue around RTL languages in SVGs when letter-spacing is negative + * https://bugs.chromium.org/p/chromium/issues/detail?id=966480 + */ +svg text { + letter-spacing: normal !important; // sass-lint:disable-line no-important +} diff --git a/src/global_styling/reset/_scrollbar.scss b/src/themes/legacy/reset/_scrollbar.scss similarity index 100% rename from src/global_styling/reset/_scrollbar.scss rename to src/themes/legacy/reset/_scrollbar.scss diff --git a/wiki/consuming.md b/wiki/consuming.md index 18afb1c1a67..bf138bfa4cc 100644 --- a/wiki/consuming.md +++ b/wiki/consuming.md @@ -50,12 +50,6 @@ Other compiled themes include: ```js import '@elastic/eui/dist/eui_theme_dark.css'; ``` -```js -import '@elastic/eui/dist/eui_theme_amsterdam_light.css'; -``` -```js -import '@elastic/eui/dist/eui_theme_amsterdam_dark.css'; -``` ### Using our Sass variables on top of compiled CSS @@ -64,22 +58,15 @@ If you want to build **on top** of the EUI theme by accessing the Sass variables First import the correct colors file, followed by the globals file. ```scss -@import '@elastic/eui/src/themes/eui/eui_colors_light.scss'; -@import '@elastic/eui/src/themes/eui/eui_globals.scss'; +@import '@elastic/eui/src/themes/amsterdam/_colors_light.scss'; +@import '@elastic/eui/src/themes/amsterdam/_globals.scss'; ``` For the dark theme, swap the first import for the dark colors file. ```scss -@import '@elastic/eui/src/themes/eui/eui_colors_dark.scss'; -@import '@elastic/eui/src/themes/eui/eui_globals.scss'; -``` - -If you want to use the new, but in progress Amsterdam theme, you can import it similarly. - -```scss -@import '@elastic/eui/src/themes/eui-amsterdam/eui_amsterdam_colors_light.scss'; -@import '@elastic/eui/src/themes/eui-amsterdam/eui_amsterdam_globals.scss'; +@import '@elastic/eui/src/themes/amsterdam/_colors_dark.scss'; +@import '@elastic/eui/src/themes/amsterdam/_globals.scss'; ``` ### Using Sass to customize EUI @@ -101,14 +88,6 @@ $euiColorPrimary: #7B61FF; By default, EUI ships with a font stack that includes some outside, open source fonts. If your system is internet available you can include these by adding the following imports to your SCSS/CSS files, otherwise you'll need to bundle the physical fonts in your build. EUI will drop to System Fonts (which you may prefer) in their absence. -```scss -// index.scss -@import url('https://fonts.googleapis.com/css?family=Roboto+Mono:400,400i,700,700i'); -@import url('https://rsms.me/inter/inter-ui.css'); -``` - -The Amsterdam theme uses the latest version of Inter that can be grabbed from Google Fonts as well. - ```scss // index.scss @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap'); diff --git a/yarn.lock b/yarn.lock index 746a4da9dca..da18676d21b 100755 --- a/yarn.lock +++ b/yarn.lock @@ -1141,6 +1141,17 @@ "@emotion/weak-memoize" "^0.2.5" stylis "^4.0.3" +"@emotion/cache@^11.4.0": + version "11.4.0" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.4.0.tgz#293fc9d9a7a38b9aad8e9337e5014366c3b09ac0" + integrity sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g== + dependencies: + "@emotion/memoize" "^0.7.4" + "@emotion/sheet" "^1.0.0" + "@emotion/utils" "^1.0.0" + "@emotion/weak-memoize" "^0.2.5" + stylis "^4.0.3" + "@emotion/css-prettifier@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@emotion/css-prettifier/-/css-prettifier-1.0.0.tgz#3ed4240d93c9798c001cedf27dd0aa960bdddd1a"