{field.fields.map((f, i) => {
const key = getFieldKey(formType, f, i);
diff --git a/frontend/src/js/headings/Headings.tsx b/frontend/src/js/headings/Headings.tsx
index 834f63e5a6..018e5d315d 100644
--- a/frontend/src/js/headings/Headings.tsx
+++ b/frontend/src/js/headings/Headings.tsx
@@ -1,5 +1,13 @@
-import tw from "tailwind-styled-components";
+import type { ComponentProps } from "react";
+import { tv } from "tailwind-variants";
-export const Heading3 = tw("h3")`text-lg font-normal text-gray-800`;
+const heading3 = tv({ base: "text-lg font-normal text-gray-800" });
+const heading4 = tv({ base: "text-sm font-normal text-gray-500 uppercase" });
-export const Heading4 = tw("h4")`text-sm font-normal text-gray-500 uppercase`;
+export const Heading3 = ({ className, ...props }: ComponentProps<"h3">) => (
+
+);
+
+export const Heading4 = ({ className, ...props }: ComponentProps<"h4">) => (
+
+);
diff --git a/frontend/src/js/index.tsx b/frontend/src/js/index.tsx
index 35091c3764..170336c327 100644
--- a/frontend/src/js/index.tsx
+++ b/frontend/src/js/index.tsx
@@ -7,7 +7,6 @@ import "../index.css";
import AppRoot from "./AppRoot";
import type { StateT } from "./app/reducers";
-import GlobalStyles from "./GlobalStyles";
import { makeStore } from "./store";
// TODO: OG image required?
@@ -23,7 +22,6 @@ const renderRoot = (theme: Theme) => {
return root.render(
-
,
);
diff --git a/frontend/src/js/pane/TabNavigation.tsx b/frontend/src/js/pane/TabNavigation.tsx
index d0b153cd51..c2ac05ac21 100644
--- a/frontend/src/js/pane/TabNavigation.tsx
+++ b/frontend/src/js/pane/TabNavigation.tsx
@@ -1,7 +1,7 @@
import styled from "@emotion/styled";
import { faSpinner } from "@fortawesome/free-solid-svg-icons";
-import tw from "tailwind-styled-components";
+import { tv } from "tailwind-variants";
import FaIcon from "../icon/FaIcon";
import { HoverNavigatable } from "../small-tab-navigation/HoverNavigatable";
import WithTooltip from "../tooltip/WithTooltip";
@@ -14,29 +14,28 @@ const Root = styled("div")`
align-items: flex-start;
`;
-const Headline = tw("h2")<{ $active: boolean }>`
- text-sm
- mb-0
- mt-[6px]
- mr-[5px]
- px-3
- font-bold
- leading-[30px]
- uppercase
- shrink-0
- transition-colors
- cursor-pointer
- tracking-wider
-
- border-b-[3px]
- ${({ $active }) =>
- $active ? "text-primary-500" : "text-gray-500 hover:text-black"};
- ${({ $active }) =>
- $active
- ? "border-primary-500"
- : "border-transparent hover:border-primary-200"};
-
-`;
+const headline = tv({
+ base: [
+ "mt-[6px] mr-[5px] mb-0",
+ "px-3",
+ "shrink-0",
+ "cursor-pointer",
+ "border-b-[3px]",
+ "transition-colors",
+ "text-sm",
+ "leading-[30px]",
+ "font-bold",
+ "uppercase",
+ "tracking-wider",
+ ],
+ variants: {
+ active: {
+ true: "border-primary-500 text-primary-500",
+ false:
+ "border-transparent text-gray-500 hover:border-primary-200 hover:text-black",
+ },
+ },
+});
const SxWithTooltip = styled(WithTooltip)`
flex-shrink: 0;
@@ -78,13 +77,14 @@ const TabNavigation = ({
return (
-
{label}
{loading && }
-
+
);
diff --git a/frontend/src/js/previous-queries/upload/CSVColumnPicker.tsx b/frontend/src/js/previous-queries/upload/CSVColumnPicker.tsx
index c56ff41e63..50550167e6 100644
--- a/frontend/src/js/previous-queries/upload/CSVColumnPicker.tsx
+++ b/frontend/src/js/previous-queries/upload/CSVColumnPicker.tsx
@@ -12,8 +12,8 @@ import { saveAs } from "file-saver";
import type { TFunction } from "i18next";
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
+import { tv } from "tailwind-variants";
-import tw from "tailwind-styled-components";
import type { QueryUploadConfigT, UploadQueryResponseT } from "../../api/types";
import IconButton from "../../button/IconButton";
import PrimaryButton from "../../button/PrimaryButton";
@@ -32,13 +32,9 @@ const Row = styled("div")`
margin-bottom: 15px;
`;
-const Td = tw("td")`
- text-xs
- text-ellipsis
- overflow-hidden
- whitespace-nowrap
- min-w-[150px]
-`;
+const td = tv({
+ base: "min-w-[150px] overflow-hidden text-ellipsis whitespace-nowrap text-xs",
+});
const Th = styled("th")`
font-size: ${({ theme }) => theme.font.xs};
vertical-align: top;
@@ -230,18 +226,18 @@ const CSVPreviewTable = ({
csv.slice(1, 6).map((row, j) => (
{row.map((cell, i) => (
- |
+ |
{cell}
- |
+
))}
))}
{csv.length > 6 && (
{new Array(csv[0].length).fill(null).map((_, j) => (
- |
+ |
...
- |
+
))}
)}
diff --git a/frontend/src/js/snack-message/SnackMessage.tsx b/frontend/src/js/snack-message/SnackMessage.tsx
index a7adbac842..9a1d1293d4 100644
--- a/frontend/src/js/snack-message/SnackMessage.tsx
+++ b/frontend/src/js/snack-message/SnackMessage.tsx
@@ -1,31 +1,33 @@
import { faTimes } from "@fortawesome/free-solid-svg-icons";
import { memo, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
-import tw from "tailwind-styled-components";
+import { tv } from "tailwind-variants";
import type { StateT } from "../app/reducers";
import { useClickOutside } from "../common/helpers/useClickOutside";
import FaIcon from "../icon/FaIcon";
import { resetMessage as resetMessageAction } from "./actions";
import type { SnackMessageStateT } from "./reducer";
-const Root = tw("div")<{ $success?: boolean }>`
- fixed
- z-10
- bottom-5
- right-5
- text-white
- flex items-start
- max-w-[500px]
- rounded-lg
- ${({ $success }) => ($success ? "bg-primary-500/90" : "bg-black/75")}
-`;
+const root = tv({
+ base: [
+ "fixed right-5 bottom-5",
+ "z-10",
+ "flex items-start",
+ "max-w-[500px]",
+ "rounded-lg",
+ "text-white",
+ ],
+ variants: {
+ success: {
+ true: "bg-primary-500/90",
+ false: "bg-black/75",
+ },
+ },
+});
-const ClearZone = tw("div")`
- absolute top-3 right-4
- z-[11]
- cursor-pointer
- opacity-80 hover:opacity-100
-`;
+const clearZone = tv({
+ base: ["absolute top-3 right-4", "z-11", "opacity-80 hover:opacity-100"],
+});
export const SnackMessage = memo(function SnackMessageComponent() {
const ref = useRef(null);
@@ -44,15 +46,19 @@ export const SnackMessage = memo(function SnackMessageComponent() {
return (
{message && (
-
+
{/* biome-ignore lint/security/noDangerouslySetInnerHtml: messages are our own i18n text */}
-
+
+
-
+
)}
);
diff --git a/frontend/src/js/tooltip/TooltipHeader.tsx b/frontend/src/js/tooltip/TooltipHeader.tsx
index a6a9efefd5..9b0f680ba5 100644
--- a/frontend/src/js/tooltip/TooltipHeader.tsx
+++ b/frontend/src/js/tooltip/TooltipHeader.tsx
@@ -3,24 +3,25 @@ import { faAngleLeft } from "@fortawesome/free-solid-svg-icons";
import { memo } from "react";
import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
-import tw from "tailwind-styled-components";
+import { tv } from "tailwind-variants";
import IconButton from "../button/IconButton";
import { toggleDisplayTooltip } from "./actions";
-const Header = tw("h2")`
- bg-white
- h-[40px]
- shrink-0
- flex items-center
- px-5
- pt-1
- text-sm
- tracking-[1px]
- uppercase
- text-primary-500
- border-b border-gray-100
- font-bold
-`;
+const header = tv({
+ base: [
+ "flex items-center",
+ "h-[40px]",
+ "shrink-0",
+ "border-b border-gray-100",
+ "bg-white",
+ "px-5 pt-1",
+ "text-sm",
+ "font-bold",
+ "uppercase",
+ "tracking-[1px]",
+ "text-primary-500",
+ ],
+});
const StyledIconButton = styled(IconButton)`
position: absolute;
@@ -43,7 +44,7 @@ export const TooltipHeader = memo(() => {
onClick={onToggleDisplayTooltip}
icon={faAngleLeft}
/>
-
+
{t("tooltip.headline")}
>
);
});
diff --git a/frontend/src/js/tooltip/WithTooltip.tsx b/frontend/src/js/tooltip/WithTooltip.tsx
index 010462290b..741aacd9ea 100644
--- a/frontend/src/js/tooltip/WithTooltip.tsx
+++ b/frontend/src/js/tooltip/WithTooltip.tsx
@@ -1,31 +1,10 @@
-import { css, useTheme } from "@emotion/react";
+import { useTheme } from "@emotion/react";
import styled from "@emotion/styled";
import Tippy, { type TippyProps } from "@tippyjs/react";
import { memo, type ReactElement, type Ref, useMemo } from "react";
import "tippy.js/dist/tippy.css";
import "tippy.js/themes/light.css";
-/* !important: to override inline styles by tippyjs/react */
-export const tippyjsReactOverrides = css`
- div[data-tippy-root] {
- max-width: 700px;
- box-shadow: 0 0 8px rgba(0, 0, 0, 0.18);
- border-radius: 3px;
-
- > div {
- box-shadow: none;
- max-width: inherit !important;
- width: 100%;
- padding: 0;
- }
-
- .tippy-content {
- padding: 0px;
- box-shadow: none;
- }
- }
-`;
-
const Text = styled("div")<{ wide?: boolean }>`
max-width: ${({ wide }) => (wide ? "700px" : "400px")};
text-align: left;