diff --git a/.demo/social-preview.png b/.demo/social-preview.png new file mode 100644 index 00000000000..21dc46b7b33 Binary files /dev/null and b/.demo/social-preview.png differ diff --git a/.eslintignore b/.eslintignore index 23fbaa61b7b..2c03c06d218 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,3 +6,4 @@ public .cache .eslintrc.js *.d.ts +*.js diff --git a/.eslintrc.js b/.eslintrc.js index b54ad07aa26..1310562e000 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,10 +4,17 @@ module.exports = { rules: { 'jest/no-export': 'off', 'jest/expect-expect': 'off', + 'jest/valid-title': 'off', 'react/jsx-pascal-case': 'off', 'newline-per-chained-call': 'off', + 'import/extensions': 'off', + 'jsx-a11y/label-has-associated-control': 'off', + 'react/self-closing-comp': 'off', + 'react/jsx-closing-bracket-location': 'off', + '@typescript-eslint/no-loop-func': 'off', + 'no-restricted-syntax': 'off', }, parserOptions: { - project: './tsconfig.json', + project: './tsconfig.eslint.json', }, }; diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index f26a778cf35..0fb3ce439a1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,5 +1,6 @@ name: Report an issue with @mantine/ scoped package description: Tell us if something is not working as expected +labels: 'review required' body: - type: markdown attributes: @@ -17,12 +18,15 @@ body: - '@mantine/form' - '@mantine/notifications' - '@mantine/tiptap' - - '@mantine/prism' + - '@mantine/code-highlight' - '@mantine/modals' - '@mantine/dropzone' - '@mantine/spotlight' - '@mantine/nprogress' - '@mantine/carousel' + - '@mantine/colors-generator' + - '@mantine/store' + - '@mantine/vanilla-extract' validations: required: true - type: textarea @@ -34,7 +38,7 @@ body: - type: input id: version attributes: - label: What version of @mantine/hooks page do you have in package.json? + label: What version of @mantine/* page do you have in package.json? (Note that all @mantine/* packages must have the same version in order to work correctly) validations: required: true - type: input diff --git a/.github/ISSUE_TEMPLATE/docs_report.yml b/.github/ISSUE_TEMPLATE/docs_report.yml index 419f85294d7..c4b4fb8a497 100644 --- a/.github/ISSUE_TEMPLATE/docs_report.yml +++ b/.github/ISSUE_TEMPLATE/docs_report.yml @@ -1,5 +1,6 @@ name: Report an issue with mantine.dev website description: Nothing is perfect, especially our docs, help us find and fix mistakes, bad wording, etc. +labels: 'review required' body: - type: markdown attributes: diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 3a86313fc72..0275c67cde4 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: - node-version: '16.10.0' + node-version: '18.17.0' cache: 'yarn' cache-dependency-path: '**/yarn.lock' - name: Install dependencies diff --git a/.gitignore b/.gitignore index 7180eef5c37..bc1afb58030 100644 --- a/.gitignore +++ b/.gitignore @@ -34,9 +34,8 @@ docs/.docgen/ # project .eslintcache docs/.cache -docs/public storybook-static .next -docs/src/components/Test.tsx -____*.internal.ts -____*.internal.tsx +docs/components/___test.internal.tsx +src/*/styles.css +.stylelintcache diff --git a/.nvmrc b/.nvmrc index 53a42214a46..1df6fd41c70 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.13.2 +v20.5.0 diff --git a/.storybook/main.js b/.storybook/main.js new file mode 100644 index 00000000000..0b351349648 --- /dev/null +++ b/.storybook/main.js @@ -0,0 +1,52 @@ +const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin').default; +const path = require('path'); + +module.exports = { + stories: ['../src/**/*.story.@(js|jsx|ts|tsx)'], + addons: [ + 'storybook-dark-mode', + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + { + name: 'storybook-css-modules', + options: { + cssModulesLoaderOptions: { + importLoaders: 1, + modules: { + localIdentName: 'mantine-[hash:base64:7]', + }, + }, + }, + }, + { + name: '@storybook/addon-postcss', + options: { + postcssLoaderOptions: { + implementation: require('postcss'), + }, + }, + }, + ], + framework: '@storybook/react', + core: { + builder: '@storybook/builder-webpack5', + }, + webpackFinal: async (config) => { + config.resolve = { + ...config.resolve, + plugins: [ + ...(config.resolve.plugins || []), + new TsconfigPathsPlugin({ + extensions: ['.ts', '.tsx', '.js'], + configFile: path.join(__dirname, '../tsconfig.json'), + }), + ], + }; + + // Turn off docgen plugin as it breaks bundle with displayName + config.plugins.pop(); + + return config; + }, +}; diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 00000000000..78d2eb368fc --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,58 @@ +import React, { useEffect } from 'react'; +import addons from '@storybook/addons'; +import { IconTextDirectionLtr, IconTextDirectionRtl } from '@tabler/icons-react'; +import { DARK_MODE_EVENT_NAME } from 'storybook-dark-mode'; +import { + MantineProvider, + useMantineColorScheme, + ActionIcon, + DirectionProvider, + useDirection, +} from '@mantine/core'; +import { Notifications } from '@mantine/notifications'; +import { theme } from '../docs/theme'; + +export const parameters = { layout: 'fullscreen' }; + +const channel = addons.getChannel(); + +function ColorSchemeWrapper({ children }: { children: React.ReactNode }) { + const { setColorScheme } = useMantineColorScheme(); + const handleColorScheme = (value: boolean) => setColorScheme(value ? 'dark' : 'light'); + + useEffect(() => { + channel.on(DARK_MODE_EVENT_NAME, handleColorScheme); + return () => channel.off(DARK_MODE_EVENT_NAME, handleColorScheme); + }, [channel]); + + return {children}; +} + +function DirectionWrapper({ children }: { children: React.ReactNode }) { + const { dir, toggleDirection } = useDirection(); + return ( + <> + + {dir === 'ltr' ? : } + + + {children} + + ); +} + +export const decorators = [ + (renderStory: any) => {renderStory()}, + (renderStory: any) => {renderStory()}, + (renderStory: any) => {renderStory()}, +]; diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 00000000000..f49d0a4b522 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,4 @@ +src/*/esm/**/*.css +src/*/cjs/**/*.css +docs/.next +docs/out diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 00000000000..4ea6506d911 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,28 @@ +{ + "extends": ["stylelint-config-standard-scss"], + "rules": { + "custom-property-pattern": null, + "selector-class-pattern": null, + "scss/no-duplicate-mixins": null, + "declaration-empty-line-before": null, + "declaration-block-no-redundant-longhand-properties": null, + "alpha-value-notation": null, + "custom-property-empty-line-before": null, + "property-no-vendor-prefix": null, + "color-function-notation": null, + "length-zero-no-unit": null, + "selector-not-notation": null, + "no-descending-specificity": null, + "comment-empty-line-before": null, + "scss/at-mixin-pattern": null, + "scss/at-rule-no-unknown": null, + "value-keyword-case": null, + "media-feature-range-notation": null, + "selector-pseudo-class-no-unknown": [ + true, + { + "ignorePseudoClasses": ["global"] + } + ] + } +} diff --git a/.syncpackrc.json b/.syncpackrc.json index 4df8d337c94..fc66d432148 100644 --- a/.syncpackrc.json +++ b/.syncpackrc.json @@ -1,6 +1,27 @@ { - "dev": true, - "peer": false, - "prod": true, - "source": ["package.json", "src/*/package.json", "docs/package.json"] + "dependencyTypes": ["dev", "prod", "peer"], + "source": ["package.json", "src/*/package.json", "docs/package.json"], + "versionGroups": [ + { + "packages": ["docs"], + "dependencies": [ + "@mantine/store", + "@mantine/styles-api", + "@mantine/code-highlight", + "@mantine/core", + "@mantine/ds", + "@mantine/hooks", + "@mantine/notifications", + "@mantine/spotlight", + "@mantine/carousel", + "@mantine/dropzone", + "@mantine/form", + "@mantine/nprogress", + "@mantine/dates", + "@mantine/modals", + "@mantine/tiptap" + ], + "isIgnored": true + } + ] } diff --git a/.tool-versions b/.tool-versions index 1237b21d3fa..f62fc07b5d2 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 16.13.2 +nodejs 14.17.0 diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 664a520c1bc..00000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "recommendations": [ - "formulahendry.auto-rename-tag", - "streetsidesoftware.code-spell-checker", - "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode", - "firsttris.vscode-jest-runner", - "meganrogge.template-string-converter", - "silvenon.mdx" - ] -} diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 231838c4500..00000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Debug Jest Tests", - "type": "node", - "request": "launch", - "runtimeArgs": ["--inspect-brk", "${workspaceRoot}/node_modules/.bin/jest", "--runInBand"], - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "port": 9229 - } - ] -} diff --git a/changelog/7.0.0.md b/changelog/7.0.0.md new file mode 100644 index 00000000000..379839fd7db --- /dev/null +++ b/changelog/7.0.0.md @@ -0,0 +1,1099 @@ +## Migration to native CSS + +Mantine no longer depends on [Emotion](https://emotion.sh/) for styles generation. All `@mantine/*` +packages are now shipped with native CSS files which can be imported from `@mantine/{package}/styles.css`, +for example: + +```tsx +import '@mantine/core/styles.css'; +``` + +This change improves performance, reduces bundle size of the library and allows using Mantine +in environments where CSS-in-JS is not supported (or supported with limitations), for example, +Next.js app directory. + +Important breaking changes: + +- `createStyles` function is no longer available, use [CSS modules](https://mantine.dev/styles/css-modules) or any other styling solution of your choice instead +- Components no longer support `sx` prop. You can use `className` or `style` props instead. +- `styles` prop no longer supports nested selectors + +It is now recommended to use [CSS modules](https://mantine.dev/styles/css-modules) to style Mantine components. +To update your project to [CSS modules](https://mantine.dev/styles/css-modules), follow the [6.x → 7.x migration guide](https://mantine.dev/guides/6x-to-7x). + +## Vanilla extract integration + +If you prefer CSS-in-JS syntax for styling, you can use [Vanilla extract](https://mantine.dev/styles/vanilla-extract) +as a TypeScript CSS preprocessor. You will be able to use most of Mantine styling features +with [Vanilla extract](https://mantine.dev/styles/vanilla-extract). + +## System color scheme support + +All components now support system color scheme – when `colorScheme` value is `auto`, +components will use `prefers-color-scheme` media query to determine if the user prefers light or dark color scheme. + +Note that `auto` is not the default value. You need to set it manually to enable system color scheme support +both on [MantineProvider](https://mantine.dev/theming/mantine-provider) and in [ColorSchemeScript](https://mantine.dev/theming/color-schemes#colorschemescript): + +```tsx +import { MantineProvider, ColorSchemeScript } from '@mantine/core'; + +function Demo() { + return ( + <> + + + + + + ); +} +``` + +## Built-in color scheme manager + +[MantineProvider](https://mantine.dev/theming/mantine-provider) now comes with a built-in color scheme manager. +It is no longer required to use any other components to set up color scheme logic. +Color scheme can be changed with [useMantineColorScheme hook](https://mantine.dev/theming/color-schemes#use-mantine-color-scheme-hook): + +```tsx +import { useMantineColorScheme, Button, Group } from '@mantine/core'; + +function Demo() { + const { setColorScheme, clearColorScheme } = useMantineColorScheme(); + + return ( + + + + + + + ); +} +``` + +## CSS modules and PostCSS preset + +[CSS modules](https://mantine.dev/styles/css-modules) is now the recommended way to style Mantine components, +although it is not required – you can choose any other styling solution of your choice. + +It is also recommended to use [postcss-preset-mantine](https://mantine.dev/styles/postcss-preset). It includes +mixins and functions to simplify styling of Mantine components. [postcss-preset-mantine](https://mantine.dev/styles/postcss-preset) +is included in all templates. + +## Global styles + +Mantine no longer includes normalize.css. Instead, it uses a bare minimum set of [global styles](https://mantine.dev/styles/global-styles). +These styles are part of the `@mantine/core` package and are applied automatically when you import +`@mantine/core/styles.css` in your application. Note that these styles cannot be decoupled from the +rest of the library. + +## Mantine as a headless UI library + +You can now use Mantine as a [headless](https://mantine.dev/styles/unstyled) library. To achieve that, just do not import +`@mantine/*/styles.css` in your application. Then you will be able to apply styles with +[Styles API](https://mantine.dev/styles/styles-api). + +## createTheme function + +`createTheme` function is a replacement for `MantineThemeOverride` type. Use it to create a theme +override, it will give you autocomplete for all theme properties: + +```tsx +import { createTheme, MantineProvider } from '@mantine/core'; + +const theme = createTheme({ + fontFamily: 'sans-serif', + primaryColor: 'orange', +}); + +function Demo() { + return ( + + + + ); +} +``` + +## Components extend functions + +All components that support [default props](https://mantine.dev/theming/default-props) or [Styles API](https://mantine.dev/styles/styles-api) now have a static `extend` function which allows getting autocomplete when customizing +[defaultProps](https://mantine.dev/theming/default-props), [classNames and styles](https://mantine.dev/styles/styles-api) of the component +on [theme](https://mantine.dev/theming/theme-object): + +```tsx +import { useState } from 'react'; +import { TextInput, MantineProvider, createTheme } from '@mantine/core'; +import classes from './Demo.module.css'; + +const theme = createTheme({ + components: { + TextInput: TextInput.extends({ + styles: (theme, props) => ({ + input: { + fontSize: props.size === 'compact' ? theme.fontSizes.sm : undefined, + } + }) + classNames: { + root: classes.root, + input: classes.input, + label: classes.label, + }, + + defaultProps: { + size: 'compact', + }, + }), + }, +}); + +function Demo() { + return ( + + + + ); +} +``` + +## classNames based on component props + +You can now get component props in [classNames and styles](https://mantine.dev/styles/styles-api) to conditionally apply styles. +This feature is a more powerful replacement for styles params. + +```tsx +import cx from 'clsx'; +import { MantineProvider, createTheme, TextInput } from '@mantine/core'; +import classes from './Demo.module.css'; + +const theme = createTheme({ + components: { + TextInput: TextInput.extend({ + classNames: (_theme, props) => ({ + label: cx({ [classes.labelRequired]: props.required }), + input: cx({ [classes.inputError]: props.error }), + }), + }), + }, +}); + +function Demo() { + return ( + + + + + ); +} +``` + +```scss +.labelRequired { + color: var(--mantine-color-red-filled); +} + +.inputError { + background-color: var(--mantine-color-red-light); +} +``` + +## Components CSS variables + +You can now customize components [CSS variables](https://mantine.dev/styles/styles-api) to change component styles based on its props. +For example, it can be used to add new [sizes](https://mantine.dev/styles/variants-sizes): + +```tsx +import { Button, rem, Group, MantineProvider, createTheme } from '@mantine/core'; + +const theme = createTheme({ + components: { + Button: Button.extend({ + vars: (theme, props) => { + if (props.size === 'xxl') { + return { + root: { + '--button-height': rem(60), + '--button-padding-x': rem(30), + '--button-fz': rem(24), + }, + }; + } + + if (props.size === 'xxs') { + return { + root: { + '--button-height': rem(24), + '--button-padding-x': rem(10), + '--button-fz': rem(10), + }, + }; + } + + return { root: {} }; + }, + }), + }, +}); + +function Demo() { + return ( + + + + + + + ); +} +``` + +## New variants system + +All components now have `data-variant` attribute on the root element, even if the component does not have any predefined variants. +You can use it to apply styles to all components of the same type on [theme](https://mantine.dev/theming/theme-object): + +```tsx +import { Input, MantineProvider, createTheme } from '@mantine/core'; +import classes from './Demo.module.css'; + +// It is better to add new variants in theme.components +// This way you will be able to use them in anywhere in the app +const theme = createTheme({ + components: { + Input: Input.extend({ classNames: classes }), + }, +}); + +function Demo() { + return ( + + + + + ); +} +``` + +```scss +.input { + &[data-variant='underline'] { + border-bottom: rem(2px) solid; + border-radius: 0; + padding-left: 0; + padding-right: 0; + + @mixin light { + border-color: var(--mantine-color-gray-3); + } + + @mixin dark { + border-color: var(--mantine-color-dark-3); + } + + &:focus { + border-color: var(--mantine-color-blue-filled); + } + } +} +``` + +## New sizes system + +There are multiple ways to customize component sizes: + +- With `data-size` attribute +- With component [CSS variables](https://mantine.dev/styles/styles-api) +- With [static CSS variables](https://mantine.dev/styles/variants-sizes#sizes-with-static-css-variables) + +Example of customizing [Button](https://mantine.dev/core/button) size with `data-size` attribute: + +```tsx +import { Input, createTheme, MantineProvider } from '@mantine/core'; +import classes from './Demo.module.css'; + +const theme = createTheme({ + components: { + Input: Input.extend({ classNames: classes }), + }, +}); + +function Demo() { + return ( + + + + + ); +} +``` + +```scss +.wrapper { + &[data-size='xxl'] { + & .input { + padding-left: rem(28px); + padding-right: rem(28px); + height: rem(68px); + font-size: rem(28px); + } + } + + &[data-size='xxs'] { + & .input { + padding-left: rem(10px); + padding-right: rem(10px); + height: rem(28px); + font-size: rem(10px); + } + } +} +``` + +## theme.variantColorResolver + +[Button](https://mantine.dev/core/button), [Badge](https://mantine.dev/core/badge), [ActionIcon](https://mantine.dev/core/action-icon), [ThemeIcon](https://mantine.dev/core/theme-icon) and other +components now support custom variants with [variantColorResolver](https://mantine.dev/theming/colors#colors-variant-resolver) +– it supports both changing colors of existing variants and adding new variants. + +```tsx +import { + Button, + Group, + MantineProvider, + defaultVariantColorsResolver, + VariantColorsResolver, + parseThemeColor, + rem, + rgba, + darken, +} from '@mantine/core'; + +const variantColorResolver: VariantColorsResolver = (input) => { + const defaultResolvedColors = defaultVariantColorsResolver(input); + const parsedColor = parseThemeColor({ + color: input.color || input.theme.primaryColor, + theme: input.theme, + }); + + // Override some properties for variant + if (parsedColor.isThemeColor && parsedColor.color === 'lime' && input.variant === 'filled') { + return { ...defaultResolvedColors, color: 'var(--mantine-color-black)' }; + } + + // Completely override variant + if (input.variant === 'light') { + return { + background: rgba(parsedColor.value, 0.1), + hover: rgba(parsedColor.value, 0.15), + border: `${rem(1)} solid ${parsedColor.value}`, + color: darken(parsedColor.value, 0.1), + }; + } + + // Add new variants support + if (input.variant === 'danger') { + return { + background: 'var(--mantine-color-red-9)', + hover: 'var(--mantine-color-red-8)', + color: 'var(--mantine-color-white)', + border: 'none', + }; + } + + return defaultResolvedColors; +}; + +function Demo() { + return ( + + + + + + + + + + ); +} +``` + +## rem units scaling + +It is now possible to scale [rem](https://mantine.dev/styles/rem#rem-units-scaling) units. It is useful when you want to change +font-size of `html`/`:root` element and preserve Mantine components sizes. For example, if you would like to set `html` font-size to `10px` and scale Mantine components accordingly, you need +to set `scale` to `1 / (10 / 16)` (16 – default font-size) = `1 / 0.625` = `1.6`: + +```css +:root { + font-size: 10px; +} +``` + +```tsx +import { MantineProvider, createTheme } from '@mantine/core'; + +const theme = createTheme({ + scale: 1.6, +}); + +function Demo() { + return ( + + + + ); +} +``` + +## color prop improvements + +All components that support `color` prop now support the following color values: + +- Key of `theme.colors`, for example, `blue` +- `theme.colors` index reference, for example, `blue.5` +- Any valid CSS color value, for example, `#fff`, `rgba(0, 0, 0, 0.5)`, `hsl(0, 0%, 100%)` + +```tsx +import { Group, Button, Text } from '@mantine/core'; + +function Demo() { + return ( + <> + + Filled variant + + + + + + + + Light variant + + + + + + + + Outline variant + + + + + + + ); +} +``` + +## Components classes + +Classes of each component are now available in `Component.classes` object. For example, you can +find [Button](https://mantine.dev/core/button) classes in `Button.classes`. + +You can use these classes to create components with the same styles as Mantine components: + +```tsx +import { Button } from '@mantine/core'; + +function Demo() { + return + ); +} +``` + +## NumberInput changes + +[NumberInput](https://mantine.dev/core/number-input) was migrated to [react-number-format](https://s-yadav.github.io/react-number-format/). +It now supports more features and has improvements in cursor position management. +Due to migration, some of the props were renamed – follow the [documentation](https://mantine.dev/core/number-input) to learn about the changes. + +## Loader changes + +[Loader](https://mantine.dev/core/loader) component is now built with CSS only. This change improves performance and reduces +HTML output of the component. + +[Theme](https://mantine.dev/theming/theme-object) object no longer supports `theme.loader` property – +it is also now possible to add custom loaders on [theme](https://mantine.dev/theming/theme-object) with [default props](https://mantine.dev/theming/default-props). +Specified [Loader](https://mantine.dev/core/loader) will be used in all components that use it under the hood ([LoadingOverlay](https://mantine.dev/core/loading-overlay), [Button](https://mantine.dev/core/button), [ActionIcon](https://mantine.dev/core/action-icon), [Stepper](https://mantine.dev/core/stepper), etc.). + +## Progress changes + +[Progress](https://mantine.dev/core/progress) component now supports compound components pattern. +Advanced features that were previously implemented in [Progress](https://mantine.dev/core/progress) are now supposed to be implemented with +compound components instead. + +```tsx +import { Progress } from '@mantine/core'; + +function Demo() { + return ( + + + Documents + + + Photos + + + Other + + + ); +} +``` + +## Table changes + +[Table](https://mantine.dev/core/table) component changes: + +- [Styles API](https://mantine.dev/styles/styles-api) support +- It is now required to use compound components instead of elements: `Table.Tr`, `Table.Td`, etc. +- It is now easier to override styles – all styles are added with classes instead of deeply nested selectors on root element +- New props: `borderColor`, `withRowBorders`, `stripedColor`, `highlightOnHoverColor` +- `withBorder` prop was renamed to `withTableBorder` +- `fontSize` prop was removed, use `fz` [style prop](https://mantine.dev/styles/style-props) instead +- New `Table.ScrollContainer` component to create scrollable table + +```tsx +import { Table } from '@mantine/core'; + +function Demo() { + const rows = elements.map((element) => ( + + {element.position} + {element.name} + {element.symbol} + {element.mass} + + )); + + return ( + + + + Element position + Element name + Symbol + Atomic mass + + + {rows} +
+ ); +} +``` + +## Group changes + +[Group](https://mantine.dev/core/group) component changes: + +- `position` prop was renamed to `justify` – it now supports all `justify-content` values +- `noWrap` prop was replaced with `wrap="nowrap"`, `wrap` prop now supports all `flex-wrap` values +- `spacing` prop was replaced with `gap` +- `Group` now supports new `preventGrowOverflow` prop which allows customizing how group items will behave when they grow larger than their dedicated space + +## Tabs changes + +- Styles API selector `tabsList` renamed to `list` +- `TabProps` type was renamed to `TabsTabProps` +- `onTabChange` prop was renamed to `onChange` +- `Tabs.List` `position` prop was renamed to `justify`, it now supports all `justify-content` values + +## Button changes + +- `compact` prop was removed, use `size="compact-XXX"` instead +- `leftIcon` and `rightIcon` props were renamed to `leftSection` and `rightSection` +- `uppercase` prop was removed, use `tt` [style prop](https://mantine.dev/styles/style-props) instead +- `loaderPosition` prop was removed, [Loader](https://mantine.dev/core/loader) is now always rendered in the center to prevent layout shifts +- Styles API selectors were changed, see [Button Styles API](https://mantine.dev/core/button?t=styles-api) documentation for more details + +## AppShell changes + +[AppShell](https://mantine.dev/core/app-shell) component was completely rewritten, it now supports more features: + +- `AppShell` now uses compound components pattern: `Navbar`, `Aside`, `Header` and `Footer` are no longer exported from `@mantine/core` package. Instead, use `AppShell.Navbar`, `AppShell.Aside`, etc. +- `AppShell` now supports animations when navbar/aside are opened/closed +- Navbar/aside can now be collapsed on desktop – state is handled separately for mobile and desktop +- Header/footer can now be collapsed the same way as navbar/aside. For example, the header can be collapsed based on scroll position or direction. +- `AppShell` no longer supports `fixed` prop – all components have `position: fixed` styles, static positioning is no longer supported +- The documentation was updated, it now includes [10+ examples on a separate page](https://mantine.dev/app-shell?e=BasicAppShell) + +## SimpleGrid changes + +[SimpleGrid](https://mantine.dev/core/simple-grid) now uses object format to define grid breakpoints and spacing, +it works the same way as [style props](https://mantine.dev/styles/style-props). + +```tsx +import { SimpleGrid } from '@mantine/core'; + +function Demo() { + return ( + +
1
+
2
+
3
+
4
+
5
+
+ ); +} +``` + +## Grid changes + +[Grid](https://mantine.dev/core/grid) now uses object format in `gutter`, `offset`, `span` and order props, +all props now work the same way as [style props](https://mantine.dev/styles/style-props). + +```tsx +import { Grid } from '@mantine/core'; + +function Demo() { + return ( + + 1 + 2 + 3 + 4 + + ); +} +``` + +## Image changes + +[Image](https://mantine.dev/core/image) component changes: + +- `Image` component no longer includes `figure` and other associated elements +- `caption` prop is no longer available +- `width` and `height` props are replaced with `w` and `h` [style props](https://mantine.dev/styles/style-props) +- Placeholder functionality was replaced with fallback image + +```tsx +import { Image } from '@mantine/core'; + +function Demo() { + return ( + + ); +} +``` + +## Spotlight changes + +[Spotlight](https://mantine.dev/others/spotlight) changes: + +- The logic is no longer based on React context +- `SpotlightProvider` was renamed to `Spotlight` +- `useSpotlight` hook was removed, use `spotlight` object instead +- `actions` prop now uses a different data format +- It is now possible to have multiple spotlights in the same app +- `Spotlight` component now uses compound components pattern for advanced customization + +```tsx +import { useState } from 'react'; +import { Spotlight, spotlight } from '@mantine/spotlight'; +import { Button } from '@mantine/core'; +import { IconSearch } from '@tabler/icons-react'; + +const data = ['Home', 'About us', 'Contacts', 'Blog', 'Careers', 'Terms of service']; + +function Demo() { + const [query, setQuery] = useState(''); + + const items = data + .filter((item) => item.toLowerCase().includes(query.toLowerCase().trim())) + .map((item) => ); + + return ( + <> + + + + } /> + + {items.length > 0 ? items : Nothing found...} + + + + ); +} +``` + +## Carousel changes + +[Carousel](https://mantine.dev/others/carousel) now uses object format for responsive values in +`slideSize` and `slideGap` props instead of `breakpoints` prop: + +```tsx +import { Carousel } from '@mantine/carousel'; + +function Demo() { + return ( + + 1 + 2 + 3 + {/* ...other slides */} + + ); +} +``` + +## @mantine/prism deprecation + +`@mantine/prism` package was deprecated in favor of `@mantine/code-highlight` package. [The new package](https://mantine.dev/others/code-highlight) uses [highlight.js](https://highlightjs.org/) instead of [Prism](https://prismjs.com/). + +```tsx +import { CodeHighlightTabs } from '@mantine/code-highlight'; +import { TypeScriptIcon, CssIcon } from '@mantine/ds'; + +const tsxCode = ` +function Button() { + return ; +} +`; + +const cssCode = ` +.button { + background-color: transparent; + color: var(--mantine-color-blue-9); +} +`; + +function getFileIcon(fileName: string) { + if (fileName.endsWith('.ts') || fileName.endsWith('.tsx')) { + return ; + } + + if (fileName.endsWith('.css')) { + return ; + } + + return null; +} + +function Demo() { + return ( + + ); +} +``` + +## Fieldset component + +New [Fieldset](https://mantine.dev/core/fieldset) component: + +```tsx +import { Fieldset } from '@mantine/core'; + +function Demo() { + return ( +
+ + +
+ ); +} +``` + +## Combobox component + +The new [Combobox](https://mantine.dev/core/combobox) component allows building custom select, autocomplete, tags input, multiselect and other +similar components. It is used as a base for updated [Autocomplete](https://mantine.dev/core/autocomplete), [Select](https://mantine.dev/core/select), +[TagsInput](https://mantine.dev/core/tags-input) and [MultiSelect](https://mantine.dev/core/multi-select) components. + +[Combobox](https://mantine.dev/core/combobox) is very flexible and allows you to have full control over the component rendering and logic. +Advanced features that were previously implemented in [Autocomplete](https://mantine.dev/core/autocomplete), [Select](https://mantine.dev/core/select) +and [MultiSelect](https://mantine.dev/core/multi-select) are now supposed to be implemented with [Combobox](https://mantine.dev/core/combobox) instead. + +You can find 50+ `Combobox` examples on [this page](https://mantine.dev/combobox). + +```tsx +import { useState } from 'react'; +import { Input, InputBase, Combobox, useCombobox } from '@mantine/core'; + +const groceries = ['🍎 Apples', '🍌 Bananas', '🥦 Broccoli', '🥕 Carrots', '🍫 Chocolate']; + +function Demo() { + const combobox = useCombobox({ + onDropdownClose: () => combobox.resetSelectedOption(), + }); + + const [value, setValue] = useState(null); + + const options = groceries.map((item) => ( + + {item} + + )); + + return ( + { + setValue(val); + combobox.closeDropdown(); + }} + > + + } + onClick={() => combobox.toggleDropdown()} + > + {value || Pick value} + + + + + {options} + + + ); +} +``` + +## TagsInput component + +New [TagsInput](https://mantine.dev/core/tags-input) component based on [Combobox](https://mantine.dev/core/combobox) component. +The component is similar to [MultiSelect](https://mantine.dev/core/multi-select) but allows entering custom values. + +```tsx +import { TagsInput } from '@mantine/core'; + +function Demo() { + return ( + + ); +} +``` + +## withErrorStyles prop + +All inputs now support `withErrorStyles` prop, which allows removing error styles from the input. +It can be used to apply custom error styles without override, or use other techniques to +indicate error state. For example, it can be used to render an icon in the right section: + +```tsx +import { TextInput, rem } from '@mantine/core'; +import { IconExclamationCircle } from '@tabler/icons-react'; + +function Demo() { + return ( + <> + + + + + } + /> + + ); +} +``` + +## hiddenFrom and visibleFrom props + +All Mantine components now support `hiddenFrom` and `visibleFrom` props. +These props accept breakpoint (`xs`, `sm`, `md`, `lg`, `xl`) and hide the component when +viewport width is less than or greater than the specified breakpoint: + +```tsx +import { Button, Group } from '@mantine/core'; + +function Demo() { + return ( + + + + + + ); +} +``` + +## Other changes + +- New [VisuallyHidden](https://mantine.dev/core/visually-hidden) component +- New [ActionIcon.Group](https://mantine.dev/core/action-icon#actionicongroup) component +- [DatesProvider](https://mantine.dev/dates/dates-provider) now supports setting timezone +- All transitions are now disabled during color scheme change +- `theme.respectReducedMotion` is now set to `false` by default – it caused a lot of confusion for users who did not know about it +- [Notifications system](https://mantine.dev/others/notifications) now exports `notifications.updateState` function to update notifications state with a given callback +- [Blockquote](https://mantine.dev/core/blockquote) component has new design +- [Breadcrumbs](https://mantine.dev/core/breadcrumbs) component now supports `separatorMargin` prop +- [Tooltip](https://mantine.dev/core/tooltip) component now supports `mainAxis` and `crossAxis` offset configuration +- [Slider and RangeSlider](https://mantine.dev/core/slider) components `radius` prop now affects thumb as well as track +- [NativeSelect](https://mantine.dev/core/native-select/) component now supports setting options as `children` and options groups +- [Anchor](https://mantine.dev/core/anchor) component now supports `underline` prop which lets you configure how link will be underlined: `hover` (default), `always` or `never` +- [Affix](https://mantine.dev/core/affix) component no longer supports `target` prop, use `portalProps` instead +- [Container](https://mantine.dev/core/container) component no longer supports `sizes` prop, use [CSS variables](https://mantine.dev/styles/styles-api) to customize sizes on [theme](https://mantine.dev/theming/theme-object) instead +- `useComponentDefaultProps` hook was renamed to [useProps](https://mantine.dev/theming/default-props#useprops-hook) +- `withinPortal` prop is now true by default in all overlay components ([Popover](https://mantine.dev/core/popover), [HoverCard](https://mantine.dev/core/hover-card), [Tooltip](https://mantine.dev/core/tooltip), etc.) +- `AlphaSlider` and `HueSlider` components are no longer available, they can be used only as a part of [ColorPicker](https://mantine.dev/core/color-picker) +- [Text](https://mantine.dev/core/text) default root element is now `

` +- [Title](https://mantine.dev/core/title) no longer supports all [Text](https://mantine.dev/core/text) props, only [style props](https://mantine.dev/styles/style-props) are available +- `MediaQuery` component was removed – use [CSS modules](https://mantine.dev/styles/css-modules) to apply responsive styles +- [Highlight](https://mantine.dev/core/highlight) component prop `highlightColor` was renamed to `color` +- [Tooltip and Tooltip.Floating](https://mantine.dev/core/tooltip) components no longer support `width` prop, use `w` [style prop](https://mantine.dev/styles/style-props) instead +- [Stack](https://mantine.dev/core/stack) component `spacing` prop was renamed to `gap` +- [Input](https://mantine.dev/core/input) and other `Input` based components `icon` prop was renamed to `leftSection` +- [Loader](https://mantine.dev/core/loader) component `variant` prop was renamed to `type` +- `@mantine/core` package no longer depends on [Radix UI ScrollArea](https://www.radix-ui.com/docs/primitives/components/scroll-area#scroll-area), [ScrollArea](https://mantine.dev/core/scroll-area) component is now a native Mantine component – it reduces bundle size, allows setting CSP for styles and improves performance (all styles are now applied with classes instead of inline `