From 167153ca32e8e24ce968a8a592030dd3b288c3f0 Mon Sep 17 00:00:00 2001
From: Alexander Polyankin <alexander.polyankin@metabase.com>
Date: Fri, 25 Aug 2023 23:10:46 +0300
Subject: [PATCH] Mantine TextInput and NumberInput (#33381)

---
 .../SearchFilterModal/filters/TypeFilter.tsx  |  25 +-
 .../buttons/Button/Button.stories.mdx         |   2 +-
 .../buttons/Button/Button.styled.tsx          |   2 +-
 .../feedback/Loader/Loader.stories.mdx        |   2 +-
 .../ui/components/feedback/Loader/Loader.tsx  |   4 +-
 .../inputs/Checkbox/Checkbox.stories.mdx      | 183 +++++---
 .../inputs/Checkbox/Checkbox.styled.tsx       | 123 ++---
 .../components/inputs/Input/Input.styled.tsx  | 139 ++++++
 .../ui/components/inputs/Input/index.ts       |   1 +
 .../NumberInput/NumberInput.stories.mdx       | 435 ++++++++++++++++++
 .../inputs/NumberInput/NumberInput.styled.tsx |  45 ++
 .../ui/components/inputs/NumberInput/index.ts |   2 +
 .../components/inputs/Radio/Radio.stories.mdx | 149 +++---
 .../components/inputs/Radio/Radio.styled.tsx  |  89 +++-
 .../inputs/TextInput/TextInput.stories.mdx    | 318 +++++++++++++
 .../inputs/TextInput/TextInput.styled.tsx     |  15 +
 .../ui/components/inputs/TextInput/index.ts   |   2 +
 .../metabase/ui/components/inputs/index.ts    |   5 +-
 .../components/overlays/Menu/Menu.stories.mdx |   2 +-
 .../components/overlays/Menu/Menu.styled.tsx  |   4 +-
 .../typography/Anchor/Anchor.stories.mdx      | 114 ++---
 .../typography/Anchor/Anchor.styled.tsx       |   5 -
 .../typography/Text/Text.stories.mdx          | 150 ++++--
 .../typography/Text/Text.styled.tsx           |  13 +
 .../typography/Title/Title.stories.mdx        |  27 +-
 frontend/src/metabase/ui/theme.ts             |  10 +-
 26 files changed, 1497 insertions(+), 369 deletions(-)
 create mode 100644 frontend/src/metabase/ui/components/inputs/Input/Input.styled.tsx
 create mode 100644 frontend/src/metabase/ui/components/inputs/Input/index.ts
 create mode 100644 frontend/src/metabase/ui/components/inputs/NumberInput/NumberInput.stories.mdx
 create mode 100644 frontend/src/metabase/ui/components/inputs/NumberInput/NumberInput.styled.tsx
 create mode 100644 frontend/src/metabase/ui/components/inputs/NumberInput/index.ts
 create mode 100644 frontend/src/metabase/ui/components/inputs/TextInput/TextInput.stories.mdx
 create mode 100644 frontend/src/metabase/ui/components/inputs/TextInput/TextInput.styled.tsx
 create mode 100644 frontend/src/metabase/ui/components/inputs/TextInput/index.ts

diff --git a/frontend/src/metabase/search/components/SearchFilterModal/filters/TypeFilter.tsx b/frontend/src/metabase/search/components/SearchFilterModal/filters/TypeFilter.tsx
index 41b581f8160..c83e5e2e28f 100644
--- a/frontend/src/metabase/search/components/SearchFilterModal/filters/TypeFilter.tsx
+++ b/frontend/src/metabase/search/components/SearchFilterModal/filters/TypeFilter.tsx
@@ -30,21 +30,16 @@ export const TypeFilter: SearchFilterComponent<"type"> = ({
     <LoadingSpinner />
   ) : (
     <SearchFilterView data-testid={dataTestId} title={t`Type`}>
-      <Checkbox.Group
-        value={value}
-        onChange={onChange}
-        data-testid="type-filter-checkbox-group"
-        inputContainer={children => (
-          <TypeCheckboxGroupWrapper>{children}</TypeCheckboxGroupWrapper>
-        )}
-      >
-        {typeFilters.map(model => (
-          <Checkbox
-            key={model}
-            value={model}
-            label={getTranslatedEntityName(model)}
-          />
-        ))}
+      <Checkbox.Group value={value} onChange={onChange}>
+        <TypeCheckboxGroupWrapper data-testid="type-filter-checkbox-group">
+          {typeFilters.map(model => (
+            <Checkbox
+              key={model}
+              value={model}
+              label={getTranslatedEntityName(model)}
+            />
+          ))}
+        </TypeCheckboxGroupWrapper>
       </Checkbox.Group>
     </SearchFilterView>
   );
diff --git a/frontend/src/metabase/ui/components/buttons/Button/Button.stories.mdx b/frontend/src/metabase/ui/components/buttons/Button/Button.stories.mdx
index 424af06cf9d..9c979b12f80 100644
--- a/frontend/src/metabase/ui/components/buttons/Button/Button.stories.mdx
+++ b/frontend/src/metabase/ui/components/buttons/Button/Button.stories.mdx
@@ -69,7 +69,7 @@ Not to use:
 
 ## Docs
 
-- [Figma File](https://www.figma.com/file/Ey1rOyIxRHpmRvE9XrGyop/Buttons-%2F-Button?type=design&node-id=1-96&mode=design&t=JaO1GZU7AnpAmx4o-0)
+- [Figma File](https://www.figma.com/file/Ey1rOyIxRHpmRvE9XrGyop/Buttons-%2F-Button?type=design&node-id=1-96&mode=design&t=yaNljw178EFJeU7k-0)
 - [Mantine Button Docs](https://mantine.dev/core/button/)
 
 ## Caveats
diff --git a/frontend/src/metabase/ui/components/buttons/Button/Button.styled.tsx b/frontend/src/metabase/ui/components/buttons/Button/Button.styled.tsx
index dfbf13a3cea..9e432658e2b 100644
--- a/frontend/src/metabase/ui/components/buttons/Button/Button.styled.tsx
+++ b/frontend/src/metabase/ui/components/buttons/Button/Button.styled.tsx
@@ -19,7 +19,7 @@ export const getButtonOverrides = (): MantineThemeOverride["components"] => ({
           height: "auto",
           padding: compact ? `${rem(3)} ${rem(7)}` : `${rem(11)} ${rem(15)}`,
           fontSize: theme.fontSizes.md,
-          lineHeight: "1rem",
+          lineHeight: theme.lineHeight,
           [`&:has(.${getStylesRef("label")}:empty)`]: {
             padding: compact ? `${rem(3)} ${rem(3)}` : `${rem(11)} ${rem(11)}`,
             [`.${getStylesRef("leftIcon")}`]: {
diff --git a/frontend/src/metabase/ui/components/feedback/Loader/Loader.stories.mdx b/frontend/src/metabase/ui/components/feedback/Loader/Loader.stories.mdx
index f55b7fbf190..bdf68114ec4 100644
--- a/frontend/src/metabase/ui/components/feedback/Loader/Loader.stories.mdx
+++ b/frontend/src/metabase/ui/components/feedback/Loader/Loader.stories.mdx
@@ -27,7 +27,7 @@ Our themed wrapper around [Mantine Loader](https://mantine.dev/core/loader/).
 
 ## Docs
 
-- [Figma File](https://www.figma.com/file/NUXRUa9Ot3HvgC1WwIA4UH/Loader?node-id=1%3A96)
+- [Figma File](https://www.figma.com/file/NUXRUa9Ot3HvgC1WwIA4UH/Loader?type=design&node-id=1-96&mode=design&t=yaNljw178EFJeU7k-0)
 - [Mantine Loader Docs](https://mantine.dev/core/loader/)
 
 ## Caveats
diff --git a/frontend/src/metabase/ui/components/feedback/Loader/Loader.tsx b/frontend/src/metabase/ui/components/feedback/Loader/Loader.tsx
index 4fc9c2b1b26..97eb97af3e4 100644
--- a/frontend/src/metabase/ui/components/feedback/Loader/Loader.tsx
+++ b/frontend/src/metabase/ui/components/feedback/Loader/Loader.tsx
@@ -1,4 +1,4 @@
-import { Loader as MantineLoader } from "@mantine/core";
+import { Loader as MantineLoader, getSize } from "@mantine/core";
 import type { LoaderProps } from "@mantine/core";
 
 const SIZES: Record<string, string> = {
@@ -10,5 +10,5 @@ const SIZES: Record<string, string> = {
 };
 
 export const Loader = ({ size = "md", ...props }: LoaderProps) => (
-  <MantineLoader {...props} size={SIZES[size] ?? size} />
+  <MantineLoader {...props} size={getSize({ size, sizes: SIZES })} />
 );
diff --git a/frontend/src/metabase/ui/components/inputs/Checkbox/Checkbox.stories.mdx b/frontend/src/metabase/ui/components/inputs/Checkbox/Checkbox.stories.mdx
index 5f3003f87e1..acfdbe118f6 100644
--- a/frontend/src/metabase/ui/components/inputs/Checkbox/Checkbox.stories.mdx
+++ b/frontend/src/metabase/ui/components/inputs/Checkbox/Checkbox.stories.mdx
@@ -1,25 +1,37 @@
 import { Canvas, Story, Meta } from "@storybook/addon-docs";
-import { Checkbox } from "./";
-
-
-<Meta title="Inputs/Checkbox & Checkbox Group" component={Checkbox}
-      argTypes={
-        {
-          size: {
-            options: ["xs", "sm", "md", "lg", "xl"],
-            control: { type: 'inline-radio' }
-          },
-        }
-      }
+import { Checkbox, Stack } from "metabase/ui";
+
+export const args = {
+  label: "Label",
+  description: "",
+  disabled: false,
+  labelPosition: "right",
+};
+
+export const argTypes = {
+  label: {
+    control: { type: "text" },
+  },
+  description: {
+    control: { type: "text" },
+  },
+  disabled: {
+    control: { type: "boolean" },
+  },
+  labelPosition: {
+    options: ["left", "right"],
+    control: { type: "inline-radio" },
+  },
+};
+
+<Meta
+  title="Inputs/Checkbox"
+  component={Checkbox}
+  args={args}
+  argTypes={argTypes}
 />
 
-export const Template = args => <Checkbox.Group defaultValue={["yes"]} {...args}>
-  <Checkbox label="Yes" value="yes" />
-  <Checkbox label="No" value="no" />
-  <Checkbox label="I'm not sure" indeterminate />
-</Checkbox.Group>
-
-# Checkbox & Checkbox.Group
+# Checkbox
 
 Our themed wrapper around [Mantine Checkbox](https://mantine.dev/core/checkbox/).
 
@@ -29,87 +41,110 @@ Checkbox buttons allow users to select a single option from a list of mutually e
 
 ## Docs
 
-- [Figma File](https://www.figma.com/file/sF1qSHk6yVqO1rFgmH0nzT/Input-%2F-Checkbox?type=design&node-id=1-96&t=4851xqAoQVenSrTX-11)
+- [Figma File](https://www.figma.com/file/sF1qSHk6yVqO1rFgmH0nzT/Input-%2F-Checkbox?type=design&node-id=1-96&mode=design&t=yaNljw178EFJeU7k-0)
 - [Mantine Checkbox Docs](https://mantine.dev/core/checkbox/)
 
-## Caveats
-
-- Please don't use the `error` prop on Checkbox components (We'll standardize on this as other inputs get converted).
-
 ## Usage guidelines
 
 - **Use this component if there are more than 5 options**. If there are fewer options, feel free to check out Checkbox or Select.
 - For option ordering, try to use your best judgement on a sensible order. For example, Yes should come before No. Alphabetical ordering is usually a good fallback if there's no inherent order in your set of choices.
 - In almost all circumstances you'll want to use `<Checkbox.Group>` to provide a set of options and help with defaultValues and state management between them.
 
-## General
+## Examples
+
+export const Template = args => (
+  <Stack>
+    <Checkbox {...args} label="Default checkbox" />
+    <Checkbox {...args} label="Indeterminate checkbox" indeterminate />
+    <Checkbox
+      {...args}
+      label="Indeterminate checked checkbox"
+      checked
+      indeterminate
+    />
+    <Checkbox {...args} label="Checked checkbox" checked />
+    <Checkbox {...args} label="Disabled checkbox" disabled />
+    <Checkbox
+      {...args}
+      label="Disabled intermediate checkbox"
+      disabled
+      intermediate
+    />
+    <Checkbox
+      {...args}
+      label="Disabled indeterminate checked checkbox"
+      disabled
+      checked
+      indeterminate
+    />
+    <Checkbox {...args} label="Disabled checked checkbox" disabled checked />
+  </Stack>
+);
+
+export const Default = args => <Checkbox {...args} />;
+
+<Canvas>
+  <Story name="Default">{Default}</Story>
+</Canvas>
+
+### Checkbox.Group
+
+export const CheckboxGroup = args => (
+  <Checkbox.Group
+    defaultValue={["react"]}
+    label="An array of good frameworks"
+    description="But which one to use?"
+  >
+    <Stack mt="md">
+      <Checkbox value="react" label="React" />
+      <Checkbox value="svelte" label="Svelte" />
+      <Checkbox value="ng" label="Angular" />
+      <Checkbox value="vue" label="Vue" />
+    </Stack>
+  </Checkbox.Group>
+);
 
 <Canvas>
-  <Story name="Default">
-    {Template.bind({})}
-  </Story>
+  <Story name="Checkbox group">{CheckboxGroup}</Story>
 </Canvas>
 
-## Disabled
+### Label
+
+export const Label = args => <Template {...args} />;
+
+<Canvas>
+  <Story name="Label">{Label}</Story>
+</Canvas>
+
+#### Left label position
+
+export const LabelLeft = args => <Template {...args} labelPosition="left" />;
 
 <Canvas>
-  <Story name="Disabled">
-    <Checkbox.Group value={["yes"]}>
-      <Checkbox label="Yes" value="yes" disabled />
-      <Checkbox label="No" value="no" disabled />
-      <Checkbox label="I'm not sure" indeterminate disabled />
-    </Checkbox.Group>
-  </Story>
+  <Story name="Label, left position">{LabelLeft}</Story>
 </Canvas>
 
-## Description
+### Description
 
-If needed you can add a description value to a Checkbox button to give more context about the choice.
+export const Description = args => (
+  <Template {...args} description="Description" />
+);
 
 <Canvas>
-  <Story name="Descriptions">
-    <Checkbox.Group defaultValue={["chocolate"]}>
-      <Checkbox
-        label="Chocolate"
-        value="chocolate"
-        description="One of our most popular flavors"
-      />
-      <Checkbox
-        label="Vanilla"
-        value="vanilla"
-        description="A classic for all seasons"
-      />
-      <Checkbox
-        label="Strawberry, no description needed!"
-        value="strawberry"
-      />
-      <Checkbox
-        label="Swirl"
-        value="swirl"
-        description="Why not have it both ways?"
-      />
-    </Checkbox.Group>
-  </Story>
+  <Story name="Description">{Description}</Story>
 </Canvas>
 
-## Using the Checkbox group props
+export const DescriptionLeft = args => (
+  <Template {...args} description="Description" labelPosition="left" />
+);
 
-You can use `label` and `description` on the Checkbox.Group component to provide a label and description for the set of choices.
+#### Left label position
 
 <Canvas>
-  <Story name="Checkbox Group props">
-    <Checkbox.Group
-      label="A set of options"
-      description="You could so something like this if there was a deprecated setting that a user needs to update."
-      defaultValue={["one"]}
-    >
-      <Checkbox label="Old bad setting" value="one" disabled />
-      <Checkbox label="A better newer setting" value="two" />
-    </Checkbox.Group>
-  </Story>
+  <Story name="Description, left position">{DescriptionLeft}</Story>
 </Canvas>
 
 ## Related components
 
+- Radio
 - Select
-- Checkbox
diff --git a/frontend/src/metabase/ui/components/inputs/Checkbox/Checkbox.styled.tsx b/frontend/src/metabase/ui/components/inputs/Checkbox/Checkbox.styled.tsx
index 904ec2799f6..5228c0fb68c 100644
--- a/frontend/src/metabase/ui/components/inputs/Checkbox/Checkbox.styled.tsx
+++ b/frontend/src/metabase/ui/components/inputs/Checkbox/Checkbox.styled.tsx
@@ -1,77 +1,80 @@
-import type { MantineTheme, MantineThemeOverride } from "@mantine/core";
+import { getStylesRef, getSize, rem } from "@mantine/core";
+import type {
+  CheckboxStylesParams,
+  MantineTheme,
+  MantineThemeOverride,
+} from "@mantine/core";
 import { CheckboxIcon } from "./CheckboxIcon";
 
+const SIZES = {
+  md: rem(20),
+};
+
 export const getCheckboxOverrides = (): MantineThemeOverride["components"] => ({
   Checkbox: {
     defaultProps: {
       icon: CheckboxIcon,
       size: "md",
     },
-    styles: (theme: MantineTheme, params) => {
-      return {
-        root: {
-          marginBottom: theme.spacing.md,
-        },
-        label: {
-          fontWeight: 700,
-          color: theme.colors.text[2],
-          [`padding${params.labelPosition === "left" ? "Right" : "Left"}`]:
-            theme.spacing.sm,
-        },
-        input: {
-          borderRadius: theme.radius.xs,
-
-          "&:focus": {
-            outline: `2px solid ${theme.colors.brand[1]}`,
+    styles: (
+      theme: MantineTheme,
+      { labelPosition }: CheckboxStylesParams,
+      { size = "md" },
+    ) => ({
+      root: {
+        [`&:has(.${getStylesRef("input")}:disabled)`]: {
+          [`.${getStylesRef("label")}`]: {
+            color: theme.colors.text[0],
           },
-          "&:disabled": {
-            background: theme.colors.border[0],
-            border: 0,
-            "& + svg > *": {
-              fill: theme.colors.text[0],
-            },
+          [`.${getStylesRef("description")}`]: {
+            color: theme.colors.text[0],
+          },
+          [`.${getStylesRef("icon")}`]: {
+            color: theme.colors.text[0],
           },
-          cursor: "pointer",
-          ...(params.indeterminate && {
-            background: theme.colors.brand[1],
-            border: `1px solid ${theme.colors.brand[1]}`,
-          }),
-          transform: `scale(0.75)`,
-        },
-        icon: {
-          ...(params.indeterminate && {
-            "& > *": {
-              fill: theme.white,
-            },
-          }),
         },
-      };
-    },
-  },
-  CheckboxGroup: {
-    defaultProps: {
-      size: "md",
-    },
-    styles: (theme: MantineTheme) => {
-      /* Note: we need the ':has' selector to target the space just
-       * above the first checkbox since we don't seem to have selector
-       * or a way to use params to detect whether group label/description
-       * exists. This is a bit of a hack, but it works. */
+      },
+      inner: {
+        width: getSize({ size, sizes: SIZES }),
+        height: getSize({ size, sizes: SIZES }),
+      },
+      input: {
+        ref: getStylesRef("input"),
+        width: getSize({ size, sizes: SIZES }),
+        height: getSize({ size, sizes: SIZES }),
+        cursor: "pointer",
+        borderRadius: theme.radius.xs,
 
-      return {
-        label: {
-          fontWeight: 700,
-          color: theme.colors.text[2],
-          "&:has(+ .mantine-Checkbox-root)": {
-            marginBottom: theme.spacing.md,
+        "&:checked": {
+          borderColor: theme.colors.brand[1],
+          backgroundColor: theme.colors.brand[1],
+          [`.${getStylesRef("icon")}`]: {
+            color: theme.white,
           },
         },
-        description: {
-          "&:has(+ .mantine-Checkbox-root)": {
-            marginBottom: theme.spacing.md,
-          },
+        "&:disabled": {
+          borderColor: theme.colors.border[0],
+          backgroundColor: theme.colors.border[0],
         },
-      };
-    },
+      },
+      label: {
+        ref: getStylesRef("label"),
+        color: theme.colors.text[2],
+        fontSize: theme.fontSizes.md,
+        fontWeight: "bold",
+        lineHeight: "1rem",
+      },
+      description: {
+        ref: getStylesRef("description"),
+        color: theme.colors.text[2],
+        fontSize: theme.fontSizes.sm,
+        lineHeight: "1rem",
+        marginTop: theme.spacing.xs,
+      },
+      icon: {
+        ref: getStylesRef("icon"),
+        color: theme.colors.text[0],
+      },
+    }),
   },
 });
diff --git a/frontend/src/metabase/ui/components/inputs/Input/Input.styled.tsx b/frontend/src/metabase/ui/components/inputs/Input/Input.styled.tsx
new file mode 100644
index 00000000000..ddad72e294e
--- /dev/null
+++ b/frontend/src/metabase/ui/components/inputs/Input/Input.styled.tsx
@@ -0,0 +1,139 @@
+import { getSize, rem } from "@mantine/core";
+import type { InputStylesParams, MantineThemeOverride } from "@mantine/core";
+
+const SIZES = {
+  xs: rem(28),
+  md: rem(40),
+};
+
+const PADDING = 12;
+const DEFAULT_ICON_WIDTH = 40;
+const UNSTYLED_ICON_WIDTH = 28;
+const BORDER_WIDTH = 1;
+
+export const getInputOverrides = (): MantineThemeOverride["components"] => ({
+  Input: {
+    defaultProps: {
+      size: "md",
+    },
+    styles: (theme, { multiline }: InputStylesParams, { size = "md" }) => ({
+      input: {
+        color: theme.colors.text[2],
+        borderRadius: theme.radius.xs,
+        height: multiline ? "auto" : getSize({ size, sizes: SIZES }),
+        minHeight: getSize({ size, sizes: SIZES }),
+        "&::placeholder": {
+          color: theme.colors.text[0],
+        },
+        "&:disabled": {
+          backgroundColor: theme.colors.bg[0],
+        },
+        "&[data-invalid]": {
+          color: theme.colors.error[0],
+          borderColor: theme.colors.error[0],
+          "&::placeholder": {
+            color: theme.colors.error[0],
+          },
+        },
+      },
+      icon: {
+        color: theme.colors.text[2],
+      },
+      rightSection: {
+        color: theme.colors.text[0],
+      },
+    }),
+    sizes: {
+      xs: theme => ({
+        input: {
+          fontSize: theme.fontSizes.sm,
+          lineHeight: theme.lineHeight,
+        },
+      }),
+      md: theme => ({
+        input: {
+          fontSize: theme.fontSizes.md,
+          lineHeight: rem(24),
+        },
+      }),
+    },
+    variants: {
+      default: (
+        theme,
+        { withRightSection, rightSectionWidth }: InputStylesParams,
+      ) => ({
+        input: {
+          paddingLeft: rem(PADDING - BORDER_WIDTH),
+          paddingRight: withRightSection
+            ? typeof rightSectionWidth === "number"
+              ? rem(rightSectionWidth - BORDER_WIDTH)
+              : `calc(${rightSectionWidth} - ${BORDER_WIDTH}px)`
+            : rem(PADDING - BORDER_WIDTH),
+          borderColor: theme.colors.border[0],
+          "&:focus": {
+            borderColor: theme.colors.brand[1],
+          },
+          "&:read-only:not(:disabled)": {
+            borderColor: theme.colors.text[0],
+          },
+          "&[data-with-icon]": {
+            paddingLeft: rem(DEFAULT_ICON_WIDTH - BORDER_WIDTH),
+          },
+        },
+        icon: {
+          width: rem(DEFAULT_ICON_WIDTH),
+        },
+        rightSection: {
+          width: rightSectionWidth ?? rem(DEFAULT_ICON_WIDTH),
+        },
+      }),
+      unstyled: (
+        theme,
+        { withRightSection, rightSectionWidth }: InputStylesParams,
+      ) => ({
+        input: {
+          paddingLeft: 0,
+          paddingRight: withRightSection ? rightSectionWidth : 0,
+          "&[data-with-icon]": {
+            paddingLeft: rem(UNSTYLED_ICON_WIDTH),
+          },
+        },
+        icon: {
+          width: rem(UNSTYLED_ICON_WIDTH),
+          justifyContent: "left",
+        },
+        rightSection: {
+          width: rightSectionWidth ?? rem(UNSTYLED_ICON_WIDTH),
+          justifyContent: "right",
+        },
+      }),
+    },
+  },
+  InputWrapper: {
+    defaultProps: {
+      size: "md",
+      inputWrapperOrder: ["label", "description", "error", "input"],
+    },
+    styles: theme => ({
+      label: {
+        color: theme.colors.text[2],
+        fontSize: theme.fontSizes.sm,
+        fontWeight: "bold",
+        lineHeight: "1rem",
+      },
+      description: {
+        color: theme.colors.text[2],
+        fontSize: theme.fontSizes.xs,
+        lineHeight: "1rem",
+      },
+      error: {
+        color: theme.colors.error[0],
+        fontSize: theme.fontSizes.xs,
+        lineHeight: "1rem",
+      },
+      required: {
+        color: theme.colors.error[0],
+      },
+    }),
+  },
+});
diff --git a/frontend/src/metabase/ui/components/inputs/Input/index.ts b/frontend/src/metabase/ui/components/inputs/Input/index.ts
new file mode 100644
index 00000000000..c831b4e759d
--- /dev/null
+++ b/frontend/src/metabase/ui/components/inputs/Input/index.ts
@@ -0,0 +1 @@
+export { getInputOverrides } from "./Input.styled";
diff --git a/frontend/src/metabase/ui/components/inputs/NumberInput/NumberInput.stories.mdx b/frontend/src/metabase/ui/components/inputs/NumberInput/NumberInput.stories.mdx
new file mode 100644
index 00000000000..9564e9a1c6b
--- /dev/null
+++ b/frontend/src/metabase/ui/components/inputs/NumberInput/NumberInput.stories.mdx
@@ -0,0 +1,435 @@
+import { Canvas, Story, Meta } from "@storybook/addon-docs";
+import { Icon } from "metabase/core/components/Icon";
+import { Stack } from "metabase/ui";
+import { NumberInput } from "./";
+
+export const args = {
+  variant: "default",
+  size: "md",
+  label: "Label",
+  description: "",
+  error: "",
+  placeholder: "Placeholder",
+  disabled: false,
+  withAsterisk: false,
+  min: undefined,
+  max: undefined,
+  step: undefined,
+  hideControls: true,
+  precision: undefined,
+  decimalSeparator: undefined,
+};
+
+export const sampleArgs = {
+  value: 1234,
+  label: "Goal value",
+  description: "Constant line added as a marker to the chart",
+  placeholder: "0",
+  error: "required",
+  min: 0,
+  max: 100,
+};
+
+export const argTypes = {
+  variant: {
+    options: ["default", "unstyled"],
+    control: { type: "inline-radio" },
+  },
+  size: {
+    options: ["xs", "md"],
+    control: { type: "inline-radio" },
+  },
+  label: {
+    control: { type: "number" },
+  },
+  description: {
+    control: { type: "number" },
+  },
+  placeholder: {
+    control: { type: "number" },
+  },
+  error: {
+    control: { type: "number" },
+  },
+  disabled: {
+    control: { type: "boolean" },
+  },
+  withAsterisk: {
+    control: { type: "boolean" },
+  },
+  min: {
+    control: { type: "number" },
+  },
+  max: {
+    control: { type: "number" },
+  },
+  step: {
+    control: { type: "number" },
+  },
+  hideControls: {
+    control: { type: "boolean" },
+  },
+  precision: {
+    control: { type: "number" },
+  },
+  decimalSeparator: {
+    control: { type: "text" },
+  },
+};
+
+<Meta
+  title="Inputs/NumberInput"
+  component={NumberInput}
+  args={args}
+  argTypes={argTypes}
+/>
+
+# NumberInput
+
+Our themed wrapper around [Mantine NumberInput](https://mantine.dev/core/number-input/).
+
+## Docs
+
+- [Figma File](https://www.figma.com/file/YWyb541aUHtYXJVPzyYSbg/Input-%2F-Numbers?type=design&node-id=1-96&mode=design&t=1bDfUrJc5alZmVpx-0)
+- [Mantine NumberInput Docs](https://mantine.dev/core/number-input/)
+
+## Examples
+
+export const Default = args => <NumberInput {...args} />;
+
+export const Template = args => (
+  <Stack>
+    <NumberInput {...args} variant="default" />
+    <NumberInput {...args} variant="unstyled" />
+  </Stack>
+);
+
+export const Empty = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    placeholder={sampleArgs.placeholder}
+  />
+);
+
+export const Filled = args => (
+  <Template
+    {...args}
+    defaultValue={sampleArgs.value}
+    label={sampleArgs.label}
+    placeholder={sampleArgs.placeholder}
+  />
+);
+
+export const Asterisk = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    placeholder={sampleArgs.placeholder}
+    withAsterisk
+  />
+);
+
+export const Description = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+  />
+);
+
+export const Disabled = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    disabled
+    withAsterisk
+  />
+);
+
+export const Error = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    error={sampleArgs.error}
+    withAsterisk
+  />
+);
+
+export const Icons = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    icon={<Icon name="int" />}
+    withAsterisk
+  />
+);
+
+export const ReadOnly = args => (
+  <Template
+    {...args}
+    defaultValue={sampleArgs.value}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    icon={<Icon name="int" />}
+    readOnly
+  />
+);
+
+export const MinMax = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={`${sampleArgs.min} to ${sampleArgs.max}`}
+    icon={<Icon name="int" />}
+    min={sampleArgs.min}
+    max={sampleArgs.max}
+  />
+);
+
+export const Controls = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={`${sampleArgs.min} to ${sampleArgs.max}`}
+    icon={<Icon name="int" />}
+    min={sampleArgs.min}
+    max={sampleArgs.max}
+    hideControls={false}
+  />
+);
+
+export const Precision = args => (
+  <Template
+    {...args}
+    defaultValue={sampleArgs.value}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    icon={<Icon name="int" />}
+    precision={2}
+    decimalSeparator="."
+  />
+);
+
+export const ParserFormatter = args => (
+  <Template
+    {...args}
+    defaultValue={sampleArgs.value}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    icon={<Icon name="int" />}
+    parser={value => value.replace(/^\$/, "")}
+    formatter={value => (value != null ? `$${value}` : "$")}
+  />
+);
+
+<Canvas>
+  <Story name="Default">{Default}</Story>
+</Canvas>
+
+### Size - md
+
+export const EmptyMd = args => <Empty {...args} size="md" />;
+
+<Canvas>
+  <Story name="Empty, md">{EmptyMd}</Story>
+</Canvas>
+
+#### Filled
+
+export const FilledMd = args => <Filled {...args} size="md" />;
+
+<Canvas>
+  <Story name="Filled, md">{FilledMd}</Story>
+</Canvas>
+
+#### Asterisk
+
+export const AsteriskMd = args => <Asterisk {...args} size="md" />;
+
+<Canvas>
+  <Story name="Asterisk, md">{AsteriskMd}</Story>
+</Canvas>
+
+#### Description
+
+export const DescriptionMd = args => <Description {...args} size="md" />;
+
+<Canvas>
+  <Story name="Description, md">{DescriptionMd}</Story>
+</Canvas>
+
+#### Disabled
+
+export const DisabledMd = args => <Disabled {...args} size="md" />;
+
+<Canvas>
+  <Story name="Disabled, md">{Disabled}</Story>
+</Canvas>
+
+#### Error
+
+export const ErrorMd = args => <Error {...args} size="md" />;
+
+<Canvas>
+  <Story name="Error, md">{ErrorMd}</Story>
+</Canvas>
+
+#### Icon
+
+export const IconMd = args => <Icons {...args} size="md" />;
+
+<Canvas>
+  <Story name="Icon, md">{IconMd}</Story>
+</Canvas>
+
+#### Read only
+
+export const ReadOnlyMd = args => <ReadOnly {...args} size="md" />;
+
+<Canvas>
+  <Story name="Read only, md">{ReadOnlyMd}</Story>
+</Canvas>
+
+#### Min / max
+
+export const MinMaxMd = args => <MinMax {...args} size="md" />;
+
+<Canvas>
+  <Story name="Min / max, md">{MinMaxMd}</Story>
+</Canvas>
+
+#### Controls
+
+export const ControlsMd = args => <Controls {...args} size="md" />;
+
+<Canvas>
+  <Story name="Controls, md">{ControlsMd}</Story>
+</Canvas>
+
+#### Precision and decimal separator
+
+export const PrecisionMd = args => <Precision {...args} size="md" />;
+
+<Canvas>
+  <Story name="Precision and decimal separator, md">{PrecisionMd}</Story>
+</Canvas>
+
+#### Parser and formatter
+
+export const ParserFormatterMd = args => (
+  <ParserFormatter {...args} size="md" />
+);
+
+<Canvas>
+  <Story name="Parser and formatter, md">{ParserFormatterMd}</Story>
+</Canvas>
+
+### Size - xs
+
+export const EmptyXs = args => <Empty {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Empty, xs">{EmptyXs}</Story>
+</Canvas>
+
+#### Filled
+
+export const FilledXs = args => <Filled {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Filled, xs">{FilledXs}</Story>
+</Canvas>
+
+#### Asterisk
+
+export const AsteriskXs = args => <Asterisk {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Asterisk, xs">{AsteriskXs}</Story>
+</Canvas>
+
+#### Description
+
+export const DescriptionXs = args => <Description {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Description, xs">{DescriptionXs}</Story>
+</Canvas>
+
+#### Disabled
+
+export const DisabledXs = args => <Disabled {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Disabled, xs">{Disabled}</Story>
+</Canvas>
+
+#### Error
+
+export const ErrorXs = args => <Error {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Error, xs">{ErrorXs}</Story>
+</Canvas>
+
+#### Icon
+
+export const IconXs = args => <Icons {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Icon, xs">{IconXs}</Story>
+</Canvas>
+
+#### Read only
+
+export const ReadOnlyXs = args => <ReadOnly {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Read only, xs">{ReadOnlyXs}</Story>
+</Canvas>
+
+#### Min / max
+
+export const MinMaxXs = args => <MinMax {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Min / max, xs">{MinMaxXs}</Story>
+</Canvas>
+
+#### Controls
+
+export const ControlsXs = args => <Controls {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Controls, xs">{ControlsXs}</Story>
+</Canvas>
+
+#### Precision and decimal separator
+
+export const PrecisionXs = args => <Precision {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Precision and decimal separator, xs">{PrecisionXs}</Story>
+</Canvas>
+
+#### Parser and formatter
+
+export const ParserFormatterXs = args => (
+  <ParserFormatter {...args} size="xs" />
+);
+
+<Canvas>
+  <Story name="Parser and formatter, xs">{ParserFormatterXs}</Story>
+</Canvas>
diff --git a/frontend/src/metabase/ui/components/inputs/NumberInput/NumberInput.styled.tsx b/frontend/src/metabase/ui/components/inputs/NumberInput/NumberInput.styled.tsx
new file mode 100644
index 00000000000..15fea19a657
--- /dev/null
+++ b/frontend/src/metabase/ui/components/inputs/NumberInput/NumberInput.styled.tsx
@@ -0,0 +1,45 @@
+import { getSize, rem } from "@mantine/core";
+import type {
+  ContextStylesParams,
+  MantineThemeOverride,
+  NumberInputStylesParams,
+} from "@mantine/core";
+
+const CONTROL_SIZES = {
+  xs: rem(16),
+  md: rem(20),
+};
+
+export const getNumberInputOverrides =
+  (): MantineThemeOverride["components"] => ({
+    NumberInput: {
+      defaultProps: {
+        size: "md",
+        hideControls: true,
+      },
+      styles: (
+        theme,
+        params: NumberInputStylesParams,
+        { size = "md" }: ContextStylesParams,
+      ) => ({
+        wrapper: {
+          marginTop: theme.spacing.xs,
+        },
+        control: {
+          color: theme.colors.text[2],
+          width: getSize({ size, sizes: CONTROL_SIZES }),
+          borderColor: theme.colors.border[0],
+          "&:disabled": {
+            color: theme.colors.border[0],
+            backgroundColor: theme.colors.bg[0],
+          },
+        },
+        rightSection: {
+          width: "auto",
+          margin: 0,
+          borderTopRightRadius: theme.radius.xs,
+          borderBottomRightRadius: theme.radius.xs,
+        },
+      }),
+    },
+  });
diff --git a/frontend/src/metabase/ui/components/inputs/NumberInput/index.ts b/frontend/src/metabase/ui/components/inputs/NumberInput/index.ts
new file mode 100644
index 00000000000..660dcae3cf2
--- /dev/null
+++ b/frontend/src/metabase/ui/components/inputs/NumberInput/index.ts
@@ -0,0 +1,2 @@
+export { NumberInput } from "@mantine/core";
+export { getNumberInputOverrides } from "./NumberInput.styled";
diff --git a/frontend/src/metabase/ui/components/inputs/Radio/Radio.stories.mdx b/frontend/src/metabase/ui/components/inputs/Radio/Radio.stories.mdx
index 52ad90452c8..c264c744f27 100644
--- a/frontend/src/metabase/ui/components/inputs/Radio/Radio.stories.mdx
+++ b/frontend/src/metabase/ui/components/inputs/Radio/Radio.stories.mdx
@@ -1,9 +1,32 @@
 import { Canvas, Story, Meta } from "@storybook/addon-docs";
-import { Radio } from "./";
-
-<Meta title="Inputs/Radio & Radio Group" component={Radio} />
-
-# Radio & Radio.Group
+import { Radio, Stack } from "metabase/ui";
+
+export const args = {
+  label: "Label",
+  description: "",
+  disabled: false,
+  labelPosition: "right",
+};
+
+export const argTypes = {
+  label: {
+    control: { type: "text" },
+  },
+  description: {
+    control: { type: "text" },
+  },
+  disabled: {
+    control: { type: "boolean" },
+  },
+  labelPosition: {
+    options: ["left", "right"],
+    control: { type: "inline-radio" },
+  },
+};
+
+<Meta title="Inputs/Radio" component={Radio} args={args} argTypes={argTypes} />
+
+# Radio
 
 Our themed wrapper around [Mantine Radio](https://mantine.dev/core/radio/).
 
@@ -13,86 +36,90 @@ Radio buttons allow users to select a single option from a list of mutually excl
 
 ## Docs
 
-- [Figma File](https://www.figma.com/file/7LCGPhkbJdrhdIaeiU1O9c/Input-%2F-Radio?type=design&node-id=1%3A96&t=S6gieWhmvLP15ARp-1)
+- [Figma File](https://www.figma.com/file/7LCGPhkbJdrhdIaeiU1O9c/Input-%2F-Radio?type=design&node-id=1-96&mode=design&t=yaNljw178EFJeU7k-0)
 - [Mantine Radio Docs](https://mantine.dev/core/radio/)
 
-## Caveats
-
-- As of right now, don't use the size prop.
-- Please don't use the `error` prop on Radio components (We'll standardize on this as other inputs get converted).
-
 ## Usage guidelines
 
-- **Limit usage to 5 options max**. Radio buttons should be used when there are 2-3 options to choose from. If there are more than 5 options, consider using a [Select](../Select) or [Checkbox](../Checkbox) instead.
+- **Use this component if there are more than 5 options**. If there are fewer options, feel free to check out Radio or Select.
 - For option ordering, try to use your best judgement on a sensible order. For example, Yes should come before No. Alphabetical ordering is usually a good fallback if there's no inherent order in your set of choices.
-- In almost all circumstances you'll want to use `<Raio.Group>` to provide a set of options and help with defaultValues and state management between them.
+- In almost all circumstances you'll want to use `<Radio.Group>` to provide a set of options and help with defaultValues and state management between them.
+
+## Examples
+
+export const Template = args => (
+  <Stack>
+    <Radio {...args} label="Default radio" />
+    <Radio {...args} label="Checked radio" checked />
+    <Radio {...args} label="Disabled radio" disabled />
+    <Radio {...args} label="Disabled checked radio" disabled checked />
+  </Stack>
+);
+
+export const Default = args => <Radio {...args} />;
+
+<Canvas>
+  <Story name="Default">{Default}</Story>
+</Canvas>
 
-## General
+### Radio.Group
+
+export const RadioGroup = args => (
+  <Radio.Group
+    defaultValue={"react"}
+    label="An array of good frameworks"
+    description="But which one to use?"
+  >
+    <Stack mt="md">
+      <Radio value="react" label="React" />
+      <Radio value="svelte" label="Svelte" />
+      <Radio value="ng" label="Angular" />
+      <Radio value="vue" label="Vue" />
+    </Stack>
+  </Radio.Group>
+);
 
 <Canvas>
-  <Story name="Default">
-    <Radio.Group defaultValue="yes">
-      <Radio label="Yes" value="yes" />
-      <Radio label="No" value="no" />
-    </Radio.Group>
-  </Story>
+  <Story name="Radio group">{RadioGroup}</Story>
 </Canvas>
 
-## Disabled
+### Label
+
+export const Label = args => <Template {...args} />;
 
 <Canvas>
-  <Story name="Disabled">
-    <Radio.Group value="yes">
-      <Radio label="Yes" value="yes" disabled />
-      <Radio label="No" value="no" disabled />
-    </Radio.Group>
-  </Story>
+  <Story name="Label">{Label}</Story>
 </Canvas>
 
-## Description
+#### Left label position
 
-If needed you can add a description value to a radio button to give more context about the choice.
+export const LabelLeft = args => <Template {...args} labelPosition="left" />;
 
 <Canvas>
-  <Story name="Descriptions">
-    <Radio.Group defaultValue="chocolate">
-      <Radio
-        label="Chocolate"
-        value="chocolate"
-        description="One of our most popular flavors"
-      />
-      <Radio
-        label="Vanilla"
-        value="vanilla"
-        description="A classic for all seasons"
-      />
-      <Radio
-        label="Swirl"
-        value="swirl"
-        description="Why not have it both ways?"
-      />
-    </Radio.Group>
-  </Story>
+  <Story name="Label, left position">{LabelLeft}</Story>
 </Canvas>
 
-## Using the Radio group props
+### Description
 
-You can use `label` and `description` on the Radio.Group component to provide a label and description for the set of choices.
+export const Description = args => (
+  <Template {...args} description="Description" />
+);
 
 <Canvas>
-  <Story name="Radio Group props">
-    <Radio.Group
-      label="A set of options"
-      description="You could so something like this if there was a deprecated setting that a user needs to update."
-      defaultValue="one"
-    >
-      <Radio label="Old bad setting" value="one" disabled />
-      <Radio label="A better newer setting" value="two" />
-    </Radio.Group>
-  </Story>
+  <Story name="Description">{Description}</Story>
+</Canvas>
+
+export const DescriptionLeft = args => (
+  <Template {...args} description="Description" labelPosition="left" />
+);
+
+#### Left label position
+
+<Canvas>
+  <Story name="Description, left position">{DescriptionLeft}</Story>
 </Canvas>
 
 ## Related components
 
-- Select
 - Checkbox
+- Select
diff --git a/frontend/src/metabase/ui/components/inputs/Radio/Radio.styled.tsx b/frontend/src/metabase/ui/components/inputs/Radio/Radio.styled.tsx
index 42f4f305a09..16b91dd32ac 100644
--- a/frontend/src/metabase/ui/components/inputs/Radio/Radio.styled.tsx
+++ b/frontend/src/metabase/ui/components/inputs/Radio/Radio.styled.tsx
@@ -1,30 +1,77 @@
-import type { MantineThemeOverride } from "@mantine/core";
+import { getStylesRef, getSize, rem } from "@mantine/core";
+import type {
+  RadioStylesParams,
+  MantineTheme,
+  MantineThemeOverride,
+} from "@mantine/core";
+
+const SIZES = {
+  md: rem(20),
+};
 
 export const getRadioOverrides = (): MantineThemeOverride["components"] => ({
   Radio: {
-    styles: theme => {
-      return {
-        root: {
-          marginBottom: theme.spacing.md,
+    defaultProps: {
+      size: "md",
+    },
+    styles: (
+      theme: MantineTheme,
+      { labelPosition }: RadioStylesParams,
+      { size = "md" },
+    ) => ({
+      root: {
+        [`&:has(.${getStylesRef("input")}:disabled)`]: {
+          [`.${getStylesRef("label")}`]: {
+            color: theme.colors.text[0],
+          },
+          [`.${getStylesRef("description")}`]: {
+            color: theme.colors.text[0],
+          },
+          [`.${getStylesRef("icon")}`]: {
+            color: theme.white,
+          },
         },
-        label: {
-          color: theme.colors.text[2],
-          fontWeight: 700,
+      },
+      inner: {
+        width: getSize({ size, sizes: SIZES }),
+        height: getSize({ size, sizes: SIZES }),
+      },
+      radio: {
+        ref: getStylesRef("input"),
+        width: getSize({ size, sizes: SIZES }),
+        height: getSize({ size, sizes: SIZES }),
+        cursor: "pointer",
+        borderColor: theme.colors.text[0],
+
+        "&:checked": {
+          borderColor: theme.colors.brand[1],
+          backgroundColor: theme.colors.brand[1],
         },
-      };
-    },
-  },
-  RadioGroup: {
-    styles: theme => {
-      return {
-        label: {
-          fontWeight: 700,
-          color: theme.colors.text[2],
+        "&:disabled": {
+          opacity: 0.3,
         },
-        description: {
-          marginBottom: theme.spacing.md,
+        "&:disabled:not(:checked)": {
+          borderColor: theme.colors.text[0],
+          backgroundColor: theme.colors.bg[1],
         },
-      };
-    },
+      },
+      label: {
+        ref: getStylesRef("label"),
+        color: theme.colors.text[2],
+        fontSize: theme.fontSizes.md,
+        fontWeight: "bold",
+        lineHeight: theme.lineHeight,
+      },
+      description: {
+        ref: getStylesRef("description"),
+        color: theme.colors.text[2],
+        fontSize: theme.fontSizes.sm,
+        lineHeight: theme.lineHeight,
+        marginTop: theme.spacing.xs,
+      },
+      icon: {
+        ref: getStylesRef("icon"),
+      },
+    }),
   },
 });
diff --git a/frontend/src/metabase/ui/components/inputs/TextInput/TextInput.stories.mdx b/frontend/src/metabase/ui/components/inputs/TextInput/TextInput.stories.mdx
new file mode 100644
index 00000000000..01a2fa97731
--- /dev/null
+++ b/frontend/src/metabase/ui/components/inputs/TextInput/TextInput.stories.mdx
@@ -0,0 +1,318 @@
+import { Canvas, Story, Meta } from "@storybook/addon-docs";
+import { Icon } from "metabase/core/components/Icon";
+import { Stack } from "metabase/ui";
+import { TextInput } from "./";
+
+export const args = {
+  variant: "default",
+  size: "md",
+  label: "Label",
+  description: "",
+  error: "",
+  placeholder: "Placeholder",
+  disabled: false,
+  withAsterisk: false,
+};
+
+export const sampleArgs = {
+  value: "Metabase",
+  label: "Company or team name",
+  description: "Name used for this instance",
+  placeholder: "Department of awesome",
+  error: "required",
+};
+
+export const argTypes = {
+  variant: {
+    options: ["default", "unstyled"],
+    control: { type: "inline-radio" },
+  },
+  size: {
+    options: ["xs", "md"],
+    control: { type: "inline-radio" },
+  },
+  label: {
+    control: { type: "text" },
+  },
+  description: {
+    control: { type: "text" },
+  },
+  placeholder: {
+    control: { type: "text" },
+  },
+  error: {
+    control: { type: "text" },
+  },
+  disabled: {
+    control: { type: "boolean" },
+  },
+  withAsterisk: {
+    control: { type: "boolean" },
+  },
+};
+
+<Meta
+  title="Inputs/TextInput"
+  component={TextInput}
+  args={args}
+  argTypes={argTypes}
+/>
+
+# TextInput
+
+Our themed wrapper around [Mantine TextInput](https://mantine.dev/core/text-input/).
+
+## Docs
+
+- [Figma File](https://www.figma.com/file/oIZhYS5OoRA7twd4KqN4Eu/Input-%2F-Text?type=design&node-id=1-96&mode=design&t=yaNljw178EFJeU7k-0)
+- [Mantine TextInput Docs](https://mantine.dev/core/text-input/)
+
+## Examples
+
+export const Default = args => <TextInput {...args} />;
+
+export const Template = args => (
+  <Stack>
+    <TextInput {...args} variant="default" />
+    <TextInput {...args} variant="unstyled" />
+  </Stack>
+);
+
+export const Empty = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    placeholder={sampleArgs.placeholder}
+  />
+);
+
+export const Filled = args => (
+  <Template
+    {...args}
+    defaultValue={sampleArgs.value}
+    label={sampleArgs.label}
+    placeholder={sampleArgs.placeholder}
+  />
+);
+
+export const Asterisk = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    placeholder={sampleArgs.placeholder}
+    withAsterisk
+  />
+);
+
+export const Description = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+  />
+);
+
+export const Disabled = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    disabled
+    withAsterisk
+  />
+);
+
+export const Error = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    error={sampleArgs.error}
+    withAsterisk
+  />
+);
+
+export const Icons = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    icon={<Icon name="dashboard" />}
+    withAsterisk
+  />
+);
+
+export const RightSection = args => (
+  <Template
+    {...args}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    rightSection={<Icon name="chevrondown" />}
+    withAsterisk
+  />
+);
+
+export const ReadOnly = args => (
+  <Template
+    {...args}
+    defaultValue={sampleArgs.value}
+    label={sampleArgs.label}
+    description={sampleArgs.description}
+    placeholder={sampleArgs.placeholder}
+    rightSection={<Icon name="chevrondown" />}
+    readOnly
+  />
+);
+
+<Canvas>
+  <Story name="Default">{Default}</Story>
+</Canvas>
+
+### Size - md
+
+export const EmptyMd = args => <Empty {...args} size="md" />;
+
+<Canvas>
+  <Story name="Empty, md">{EmptyMd}</Story>
+</Canvas>
+
+#### Filled
+
+export const FilledMd = args => <Filled {...args} size="md" />;
+
+<Canvas>
+  <Story name="Filled, md">{FilledMd}</Story>
+</Canvas>
+
+#### Asterisk
+
+export const AsteriskMd = args => <Asterisk {...args} size="md" />;
+
+<Canvas>
+  <Story name="Asterisk, md">{AsteriskMd}</Story>
+</Canvas>
+
+#### Description
+
+export const DescriptionMd = args => <Description {...args} size="md" />;
+
+<Canvas>
+  <Story name="Description, md">{DescriptionMd}</Story>
+</Canvas>
+
+#### Disabled
+
+export const DisabledMd = args => <Disabled {...args} size="md" />;
+
+<Canvas>
+  <Story name="Disabled, md">{Disabled}</Story>
+</Canvas>
+
+#### Error
+
+export const ErrorMd = args => <Error {...args} size="md" />;
+
+<Canvas>
+  <Story name="Error, md">{ErrorMd}</Story>
+</Canvas>
+
+#### Icon
+
+export const IconMd = args => <Icons {...args} size="md" />;
+
+<Canvas>
+  <Story name="Icon, md">{IconMd}</Story>
+</Canvas>
+
+#### Right section
+
+export const RightSectionMd = args => <RightSection {...args} size="md" />;
+
+<Canvas>
+  <Story name="Right section, md">{RightSectionMd}</Story>
+</Canvas>
+
+#### Read only
+
+export const ReadOnlyMd = args => <ReadOnly {...args} size="md" />;
+
+<Canvas>
+  <Story name="Read only, md">{ReadOnlyMd}</Story>
+</Canvas>
+
+### Size - xs
+
+export const EmptyXs = args => <Empty {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Empty, xs">{EmptyXs}</Story>
+</Canvas>
+
+#### Filled
+
+export const FilledXs = args => <Filled {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Filled, xs">{FilledXs}</Story>
+</Canvas>
+
+#### Asterisk
+
+export const AsteriskXs = args => <Asterisk {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Asterisk, xs">{AsteriskXs}</Story>
+</Canvas>
+
+#### Description
+
+export const DescriptionXs = args => <Description {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Description, xs">{DescriptionXs}</Story>
+</Canvas>
+
+#### Disabled
+
+export const DisabledXs = args => <Disabled {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Disabled, xs">{Disabled}</Story>
+</Canvas>
+
+#### Error
+
+export const ErrorXs = args => <Error {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Error, xs">{ErrorXs}</Story>
+</Canvas>
+
+#### Icon
+
+export const IconXs = args => <Icons {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Icon, xs">{IconXs}</Story>
+</Canvas>
+
+#### Right section
+
+export const RightSectionXs = args => <RightSection {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Right section, xs">{RightSectionXs}</Story>
+</Canvas>
+
+#### Read only
+
+export const ReadOnlyXs = args => <ReadOnly {...args} size="xs" />;
+
+<Canvas>
+  <Story name="Read only, xs">{ReadOnlyXs}</Story>
+</Canvas>
diff --git a/frontend/src/metabase/ui/components/inputs/TextInput/TextInput.styled.tsx b/frontend/src/metabase/ui/components/inputs/TextInput/TextInput.styled.tsx
new file mode 100644
index 00000000000..8fe82569021
--- /dev/null
+++ b/frontend/src/metabase/ui/components/inputs/TextInput/TextInput.styled.tsx
@@ -0,0 +1,15 @@
+import type { MantineThemeOverride } from "@mantine/core";
+
+export const getTextInputOverrides =
+  (): MantineThemeOverride["components"] => ({
+    TextInput: {
+      defaultProps: {
+        size: "md",
+      },
+      styles: theme => ({
+        wrapper: {
+          marginTop: theme.spacing.xs,
+        },
+      }),
+    },
+  });
diff --git a/frontend/src/metabase/ui/components/inputs/TextInput/index.ts b/frontend/src/metabase/ui/components/inputs/TextInput/index.ts
new file mode 100644
index 00000000000..2d0e1cd158f
--- /dev/null
+++ b/frontend/src/metabase/ui/components/inputs/TextInput/index.ts
@@ -0,0 +1,2 @@
+export { TextInput } from "@mantine/core";
+export { getTextInputOverrides } from "./TextInput.styled";
diff --git a/frontend/src/metabase/ui/components/inputs/index.ts b/frontend/src/metabase/ui/components/inputs/index.ts
index 6546c7acfc0..c15c8f30500 100644
--- a/frontend/src/metabase/ui/components/inputs/index.ts
+++ b/frontend/src/metabase/ui/components/inputs/index.ts
@@ -1,2 +1,5 @@
-export * from "./Radio";
 export * from "./Checkbox";
+export * from "./Input";
+export * from "./NumberInput";
+export * from "./Radio";
+export * from "./TextInput";
diff --git a/frontend/src/metabase/ui/components/overlays/Menu/Menu.stories.mdx b/frontend/src/metabase/ui/components/overlays/Menu/Menu.stories.mdx
index e1bdc69400d..a475f110ade 100644
--- a/frontend/src/metabase/ui/components/overlays/Menu/Menu.stories.mdx
+++ b/frontend/src/metabase/ui/components/overlays/Menu/Menu.stories.mdx
@@ -81,7 +81,7 @@ Not to use:
 
 ## Docs
 
-- [Figma File](https://www.figma.com/file/MZhwfwmOaa8HeCBBUCeq7R/Menu?type=design&node-id=1-96&mode=design&t=Q0nq1hUkXN7VFjRt-0)
+- [Figma File](https://www.figma.com/file/MZhwfwmOaa8HeCBBUCeq7R/Menu?type=design&node-id=1-96&mode=design&t=vj3dPYMbYVYVuKBy-0)
 - [Mantine Menu Docs](https://mantine.dev/core/menu/)
 
 ## Caveats
diff --git a/frontend/src/metabase/ui/components/overlays/Menu/Menu.styled.tsx b/frontend/src/metabase/ui/components/overlays/Menu/Menu.styled.tsx
index 5feee98e792..cf8c695ec5b 100644
--- a/frontend/src/metabase/ui/components/overlays/Menu/Menu.styled.tsx
+++ b/frontend/src/metabase/ui/components/overlays/Menu/Menu.styled.tsx
@@ -17,7 +17,7 @@ export const getMenuOverrides = (): MantineThemeOverride["components"] => ({
         color: theme.colors.text[2],
         fontSize: theme.fontSizes.md,
         fontWeight: 700,
-        lineHeight: "1rem",
+        lineHeight: theme.lineHeight,
         padding: theme.spacing.md,
 
         "&:hover, &:focus": {
@@ -41,7 +41,7 @@ export const getMenuOverrides = (): MantineThemeOverride["components"] => ({
         color: theme.colors.text[0],
         fontSize: theme.fontSizes.md,
         fontWeight: 700,
-        lineHeight: "1rem",
+        lineHeight: theme.lineHeight,
         padding: `0.375rem ${theme.spacing.md}`,
       },
       divider: {
diff --git a/frontend/src/metabase/ui/components/typography/Anchor/Anchor.stories.mdx b/frontend/src/metabase/ui/components/typography/Anchor/Anchor.stories.mdx
index 8795ef3eafe..c1abd7265ea 100644
--- a/frontend/src/metabase/ui/components/typography/Anchor/Anchor.stories.mdx
+++ b/frontend/src/metabase/ui/components/typography/Anchor/Anchor.stories.mdx
@@ -1,10 +1,38 @@
+import { Fragment } from "react";
 import { Canvas, Story, Meta } from "@storybook/addon-docs";
-import { Anchor } from "./";
-import { Text } from "../Text"
-
-<Meta title="Typography/Anchor" component={Anchor} />
-
-export const Template = (args) => <Anchor href="https://www.youtube.com/@metabasedata" {...args} />;
+import { Anchor, Grid, Text } from "metabase/ui";
+
+export const args = {
+  size: "md",
+  align: "unset",
+  truncate: false,
+};
+
+export const sampleArgs = {
+  text: "Weniger",
+  href: "https://example.test",
+};
+
+export const argTypes = {
+  size: {
+    options: ["xs", "sm", "md", "lg"],
+    control: { type: "inline-radio" },
+  },
+  align: {
+    options: ["left", "center", "right"],
+    control: { type: "inline-radio" },
+  },
+  truncate: {
+    control: { type: "boolean" },
+  },
+};
+
+<Meta
+  title="Typography/Anchor"
+  component={Anchor}
+  args={args}
+  argTypes={argTypes}
+/>
 
 # Anchor
 
@@ -16,64 +44,42 @@ The Anchor component allows users to display links with themed styles, and repla
 
 ## Docs
 
-- [Figma File](https://www.figma.com/file/h6aMN8H67eu2w8wmDngfnM/Foundation-%2F-text?type=design&node-id=5-70&mode=design&t=9Sh3xAM7HsIZIN1z-11)
+- [Figma File](https://www.figma.com/file/8nuIBDQGSGKLfAPsebbASA/Navigation-%2F-Anchor?type=design&node-id=1-96&mode=design&t=2eUYOsqZZeMc4OGT-0)
 - [Mantine Anchor Docs](https://mantine.dev/core/anchor/)
 
-## Caveats
-
-N/A
-
-## Usage guidelines
+## Examples
 
-N/A
-
-## General
+export const Default = args => (
+  <Anchor href={sampleArgs.href}>{sampleArgs.text}</Anchor>
+);
 
 <Canvas>
-  <Story name="Default" args={{
-    children: "You should subscribe to our Youtube channel!",
-    italic: false,
-    strikethrough: false,
-    weight: "normal",
-    lineClamp: -1,
-    align: "unset",
-  }}
-  argTypes={
-   {
-     children: {
-       control: {
-         type: "text"
-       }
-     },
-     size: {
-       options: ["xs", "sm", "md", "lg", "xl"],
-       control: { type: 'inline-radio' }
-     },
-     weight: {
-       options: ["normal", "bold"],
-       control: { type: 'inline-radio' }
-     },
-     align: {
-       options: ["unset", "left", "right", "center"],
-       control: { type: 'select' }
-     }
-   }
-  }
-  >
-    {Template.bind({})}
-  </Story>
+  <Story name="Default">{Default}</Story>
 </Canvas>
 
+### Sizes
+
+export const Sizes = args => (
+  <Grid align="center" maw="18rem">
+    {argTypes.size.options.map(size => (
+      <Fragment key={size}>
+        <Grid.Col span={2}>
+          <Text weight="bold">{size}</Text>
+        </Grid.Col>
+        <Grid.Col span={10}>
+          <Anchor {...args} size={size} href={sampleArgs.href}>
+            {sampleArgs.text}
+          </Anchor>
+        </Grid.Col>
+      </Fragment>
+    ))}
+  </Grid>
+);
+
 <Canvas>
-  <Story name="Using in conjunction with Text"
-  >
-    <Text span>We've noticed that you've been looking for the latest in BI content. </Text>
-    <Anchor href="https://www.youtube.com/@metabasedata">You should subscribe to our Youtube channel!</Anchor>
-    <Text span> You won't regret it!</Text>
-  </Story>
+  <Story name="Sizes">{Sizes}</Story>
 </Canvas>
 
-
 ## Related components
 
 - Anchor
diff --git a/frontend/src/metabase/ui/components/typography/Anchor/Anchor.styled.tsx b/frontend/src/metabase/ui/components/typography/Anchor/Anchor.styled.tsx
index 7a2551211e9..1f6d4af736e 100644
--- a/frontend/src/metabase/ui/components/typography/Anchor/Anchor.styled.tsx
+++ b/frontend/src/metabase/ui/components/typography/Anchor/Anchor.styled.tsx
@@ -5,12 +5,7 @@ export const getAnchorOverrides = (): MantineThemeOverride["components"] => ({
     styles: theme => {
       return {
         root: {
-          fontFamily: "inherit",
           color: theme.colors.brand[1],
-          "&:focus": {
-            outline: `2px solid ${theme.colors.brand[0]}`,
-            outlineOffset: "2px",
-          },
           "&:active": {
             color: theme.colors.text[2],
             textDecoration: "underline",
diff --git a/frontend/src/metabase/ui/components/typography/Text/Text.stories.mdx b/frontend/src/metabase/ui/components/typography/Text/Text.stories.mdx
index 177d83a0944..3629ba08c0c 100644
--- a/frontend/src/metabase/ui/components/typography/Text/Text.stories.mdx
+++ b/frontend/src/metabase/ui/components/typography/Text/Text.stories.mdx
@@ -1,70 +1,126 @@
+import { Fragment } from "react";
 import { Canvas, Story, Meta } from "@storybook/addon-docs";
-import { Text } from "./";
-
-
-<Meta title="Typography/Text" component={Text}
-      args={{
-        children: "Having small touches of colour makes it more colourful than having the whole thing in colour",
-        underline: false,
-        italic: false,
-        strikethrough: false,
-        weight: "normal",
-        lineClamp: -1,
-        align: "unset",
-      }}
-      argTypes={
-        {
-          children: {
-            control: {
-              type: "text"
-            }
-          },
-          size: {
-            options: ["xs", "sm", "md", "lg", "xl"],
-            control: { type: 'inline-radio' }
-          },
-          weight: {
-            options: ["normal", "bold"],
-            control: { type: 'inline-radio' }
-          },
-          align: {
-            options: ["unset", "left", "right", "center"],
-            control: { type: 'select' }
-          }
-        }
-      }
+import { Box, Grid, Text } from "metabase/ui";
+
+export const args = {
+  size: "md",
+  align: "unset",
+  weight: "normal",
+  italic: false,
+  underline: false,
+  strikethrough: false,
+  truncate: false,
+  lineClamp: undefined,
+};
+
+export const sampleArgs = {
+  shortText: "Weniger",
+  longText:
+    "Having small touches of colour makes it more colourful than having the whole thing in colour",
+};
+
+export const argTypes = {
+  size: {
+    options: ["xs", "sm", "md", "lg"],
+    control: { type: "inline-radio" },
+  },
+  align: {
+    options: ["left", "center", "right"],
+    control: { type: "inline-radio" },
+  },
+  weight: {
+    options: ["normal", "bold"],
+    control: { type: "inline-radio" },
+  },
+  italic: {
+    control: { type: "boolean" },
+  },
+  underline: {
+    control: { type: "boolean" },
+  },
+  underline: {
+    control: { type: "boolean" },
+  },
+  strikethrough: {
+    control: { type: "boolean" },
+  },
+  truncate: {
+    control: { type: "boolean" },
+  },
+  lineClamp: {
+    control: { type: "number" },
+  },
+};
+
+<Meta
+  title="Typography/Text"
+  component={Text}
+  args={args}
+  argTypes={argTypes}
 />
 
-export const Template = (args) => <Text {...args} />;
-
 # Text
 
 Our themed wrapper around [Mantine Text](https://mantine.dev/core/text/).
 
 ## When to use Text
 
-The Text component allows users to display text with themed styles, and replaces the usage of `<div>text</div>` or  `<span>text</span>`. This component also handles sizing, line clamps, text decoration, and font weight. For links, use the `Anchor` component, and for code, use the `Code` component.
+The Text component allows users to display text with themed styles, and replaces the usage of `<div>text</div>` or `<span>text</span>`. This component also handles sizing, line clamps, text decoration, and font weight. For links, use the `Anchor` component, and for code, use the `Code` component.
 
 ## Docs
 
-- [Figma File](https://www.figma.com/file/h6aMN8H67eu2w8wmDngfnM/Foundation-%2F-text?type=design&node-id=5-70&mode=design&t=9Sh3xAM7HsIZIN1z-11)
+- [Figma File](https://www.figma.com/file/h6aMN8H67eu2w8wmDngfnM/Typography-%2F-Text?type=design&node-id=1-96&mode=design&t=2eUYOsqZZeMc4OGT-0)
 - [Mantine Text Docs](https://mantine.dev/core/text/)
 
-## Caveats
+## Examples
+
+export const Template = args => (
+  <Grid align="center" maw="18rem">
+    {argTypes.size.options.map(size => (
+      <Fragment key={size}>
+        <Grid.Col span={2}>
+          <Text weight="bold">{size}</Text>
+        </Grid.Col>
+        <Grid.Col span={10}>
+          <Text {...args} size={size} />
+        </Grid.Col>
+      </Fragment>
+    ))}
+  </Grid>
+);
+
+export const Default = args => <Text {...args}>{sampleArgs.shortText}</Text>;
+
+<Canvas>
+  <Story name="Default">{Default}</Story>
+</Canvas>
+
+### Sizes
+
+export const Sizes = args => (
+  <Template {...args}>{sampleArgs.shortText}</Template>
+);
 
-N/A
+<Canvas>
+  <Story name="Sizes">{Sizes}</Story>
+</Canvas>
+
+### Multiline
 
-## Usage guidelines
+export const Multiline = args => (
+  <Template {...args}>{sampleArgs.longText}</Template>
+);
+
+<Canvas>
+  <Story name="Multiline">{Multiline}</Story>
+</Canvas>
 
-N/A
+### Truncated
 
-## General
+export const Truncated = args => <Multiline {...args} lineClamp={2} />;
 
 <Canvas>
-  <Story name="Default"
-  >
-    {Template.bind({})}
-  </Story>
+  <Story name="Truncated">{Truncated}</Story>
 </Canvas>
 
 ## Related components
diff --git a/frontend/src/metabase/ui/components/typography/Text/Text.styled.tsx b/frontend/src/metabase/ui/components/typography/Text/Text.styled.tsx
index fba3ea960c9..b289d804115 100644
--- a/frontend/src/metabase/ui/components/typography/Text/Text.styled.tsx
+++ b/frontend/src/metabase/ui/components/typography/Text/Text.styled.tsx
@@ -4,6 +4,19 @@ export const getTextOverrides = (): MantineThemeOverride["components"] => ({
   Text: {
     defaultProps: {
       color: "text.2",
+      size: "md",
+    },
+    sizes: {
+      md: () => ({
+        root: {
+          lineHeight: "1.5rem",
+        },
+      }),
+      lg: () => ({
+        root: {
+          lineHeight: "1.5rem",
+        },
+      }),
     },
   },
 });
diff --git a/frontend/src/metabase/ui/components/typography/Title/Title.stories.mdx b/frontend/src/metabase/ui/components/typography/Title/Title.stories.mdx
index df04479bdf6..08f269e207d 100644
--- a/frontend/src/metabase/ui/components/typography/Title/Title.stories.mdx
+++ b/frontend/src/metabase/ui/components/typography/Title/Title.stories.mdx
@@ -1,6 +1,5 @@
 import { Canvas, Story, Meta } from "@storybook/addon-docs";
-import { Box, Stack } from "metabase/ui";
-import { Title } from "./";
+import { Box, Stack, Title } from "metabase/ui";
 
 export const args = {
   align: "left",
@@ -43,7 +42,7 @@ TBD
 
 ## Docs
 
-- [Figma File](https://www.figma.com/file/SEQS7bshKQ4y4V5FwvdROv/Typography-%2F-Title?type=design&node-id=5-70&mode=design&t=EmLhPoPqbYgYV1aP-0)
+- [Figma File](https://www.figma.com/file/SEQS7bshKQ4y4V5FwvdROv/Typography-%2F-Title?type=design&node-id=1-96&mode=design&t=2eUYOsqZZeMc4OGT-0)
 - [Mantine Title Docs](https://mantine.dev/core/title/)
 
 ## Caveats
@@ -104,28 +103,12 @@ export const Default = args => <Title {...args}>Header</Title>;
   <Story name="Default">{Default}</Story>
 </Canvas>
 
-### Left alignment
+### Sizes
 
-export const LeftAlignment = args => <Template {...args} />;
+export const Sizes = args => <Template {...args} />;
 
 <Canvas>
-  <Story name="Left alignment">{LeftAlignment}</Story>
-</Canvas>
-
-### Center alignment
-
-export const CenterAlignment = args => <Template {...args} align="center" />;
-
-<Canvas>
-  <Story name="Center alignment">{CenterAlignment}</Story>
-</Canvas>
-
-### Right alignment
-
-export const RightAlignment = args => <Template {...args} align="right" />;
-
-<Canvas>
-  <Story name="Right alignment">{RightAlignment}</Story>
+  <Story name="Sizes">{Sizes}</Story>
 </Canvas>
 
 ### Underlined
diff --git a/frontend/src/metabase/ui/theme.ts b/frontend/src/metabase/ui/theme.ts
index f2a4dfc7f3b..32619999b75 100644
--- a/frontend/src/metabase/ui/theme.ts
+++ b/frontend/src/metabase/ui/theme.ts
@@ -6,8 +6,11 @@ import {
   getAnchorOverrides,
   getButtonOverrides,
   getCheckboxOverrides,
+  getInputOverrides,
   getMenuOverrides,
+  getNumberInputOverrides,
   getRadioOverrides,
+  getTextInputOverrides,
   getTextOverrides,
   getTitleOverrides,
 } from "./components";
@@ -36,9 +39,10 @@ export const getThemeOverrides = (): MantineThemeOverride => ({
       color("bg-medium"),
       color("bg-dark"),
     ]),
+    error: getThemeColors([color("error")]),
   },
   primaryColor: "brand",
-  primaryShade: 2,
+  primaryShade: 1,
   shadows: {
     md: "0px 4px 20px 0px rgba(0, 0, 0, 0.05)",
   },
@@ -62,6 +66,7 @@ export const getThemeOverrides = (): MantineThemeOverride => ({
     lg: rem(17),
     xl: rem(21),
   },
+  lineHeight: "1rem",
   headings: {
     sizes: {
       h1: {
@@ -95,8 +100,11 @@ export const getThemeOverrides = (): MantineThemeOverride => ({
     ...getAnchorOverrides(),
     ...getButtonOverrides(),
     ...getCheckboxOverrides(),
+    ...getInputOverrides(),
     ...getMenuOverrides(),
+    ...getNumberInputOverrides(),
     ...getRadioOverrides(),
+    ...getTextInputOverrides(),
     ...getTextOverrides(),
     ...getTitleOverrides(),
   },
-- 
GitLab