diff --git a/CHANGELOG.md b/CHANGELOG.md index f528ffaec..0f57f6e1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 multiple tabs with visualize.admin or when you bookmark things. [#331](https://github.com/visualize-admin/visualization-tool/pull/331) - Theme and organization navigation counts take into account the search field now. [#329](https://github.com/visualize-admin/visualization-tool/pull/329) - Added rivers and lakes through vector layer [#309](https://github.com/visualize-admin/visualization-tool/pull/309) +- Improved chart editing navigation [#337](https://github.com/visualize-admin/visualization-tool/pull/337) +- Improved chart publish action buttons [#337](https://github.com/visualize-admin/visualization-tool/pull/337) ### Bugs diff --git a/app/components/Stack.tsx b/app/components/Stack.tsx index ed9a8476c..f450ef80f 100644 --- a/app/components/Stack.tsx +++ b/app/components/Stack.tsx @@ -1,6 +1,8 @@ import React from "react"; import { Box, BoxProps } from "theme-ui"; +type DirectionType = "row" | "column"; + const Stack = ({ children, direction, @@ -8,9 +10,10 @@ const Stack = ({ ...boxProps }: { children: React.ReactNode; - direction?: "row" | "column"; + direction?: DirectionType | DirectionType[]; spacing?: number; } & BoxProps) => { + const directions = Array.isArray(direction) ? direction : [direction]; return ( * + *:not(html)": { - [direction === "row" ? "ml" : "mt"]: spacing, + ml: directions.map((d) => (d === "row" ? spacing : 0)), + mt: directions.map((d) => (d !== "row" ? spacing : 0)), }, ...boxProps.sx, }} diff --git a/app/components/chart-panel.tsx b/app/components/chart-panel.tsx index 1774b318e..a721a57b6 100644 --- a/app/components/chart-panel.tsx +++ b/app/components/chart-panel.tsx @@ -11,6 +11,8 @@ export const ChartPanel = ({ sx={{ bg: "monochrome100", boxShadow: "primary", + borderRadius: "xl", + overflow: "hidden", width: "auto", minHeight: [150, 300, 500], borderWidth: "1px", diff --git a/app/components/header.tsx b/app/components/header.tsx index cba30f4fa..58e510e67 100644 --- a/app/components/header.tsx +++ b/app/components/header.tsx @@ -3,6 +3,59 @@ import { Trans } from "@lingui/macro"; import { Box, Flex, Text } from "theme-ui"; import { LanguageMenu } from "./language-menu"; import NextLink from "next/link"; +import React, { + Dispatch, + SetStateAction, + useContext, + useMemo, + useState, +} from "react"; + +const DEFAULT_HEADER_PROGRESS = 100; + +export const useHeaderProgressContext = () => { + const [value, setValue] = useState(DEFAULT_HEADER_PROGRESS); + return useMemo(() => ({ value, setValue }), [value, setValue]); +}; + +export const useHeaderProgress = () => useContext(HeaderProgressContext); + +const HeaderProgressContext = React.createContext({ + value: DEFAULT_HEADER_PROGRESS, + setValue: (() => undefined) as Dispatch>, +}); + +export const HeaderProgressProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const headerProgress = useHeaderProgressContext(); + return ( + + {children} + + ); +}; + +export const HeaderBorder = () => { + const { value: progress } = useHeaderProgress(); + return ( + + ); +}; export const Header = ({ pageType = "app", @@ -12,33 +65,11 @@ export const Header = ({ contentId?: string; }) => { return ( - - - - + + + + + + ); }; diff --git a/app/components/layout.tsx b/app/components/layout.tsx index f14193f54..fddb22e2c 100644 --- a/app/components/layout.tsx +++ b/app/components/layout.tsx @@ -1,19 +1,21 @@ import { Flex } from "theme-ui"; import { ReactNode } from "react"; import { Footer } from "./footer"; -import { Header } from "./header"; +import { Header, HeaderProgressProvider } from "./header"; export const AppLayout = ({ children }: { children?: ReactNode }) => ( -
- - {children} - + +
+ + {children} + + ); diff --git a/app/components/publish-actions.tsx b/app/components/publish-actions.tsx index 4f2f2e0df..cb528208c 100644 --- a/app/components/publish-actions.tsx +++ b/app/components/publish-actions.tsx @@ -1,5 +1,14 @@ import { t, Trans } from "@lingui/macro"; -import { Box, Button, Flex, Input, Link, Text } from "theme-ui"; +import { + Box, + Button, + ButtonProps, + Flex, + FlexOwnProps, + Input, + Link, + Text, +} from "theme-ui"; import * as clipboard from "clipboard-polyfill/text"; import Downshift, { DownshiftState, StateChangeOptions } from "downshift"; import { @@ -8,31 +17,35 @@ import { useEffect, useState, } from "react"; -import { Icon, IconName } from "../icons"; +import { Icon } from "../icons"; import { useLocale } from "../locales/use-locale"; import { IconLink } from "./links"; import { useI18n } from "../lib/use-i18n"; +import Stack from "./Stack"; -export const PublishActions = ({ configKey }: { configKey: string }) => { +export const PublishActions = ({ + configKey, + sx, +}: { + configKey: string; + sx?: FlexOwnProps["sx"]; +}) => { const locale = useLocale(); return ( - - {/* */} + - + ); }; const PopUp = ({ - triggerLabel, - triggerIconName, children, + renderTrigger, }: { - triggerLabel: string | ReactNode; - triggerIconName: IconName; children: ReactNode; + renderTrigger: (toggleProps: ButtonProps) => React.ReactNode; }) => { const [menuIsOpen, toggle] = useState(false); const handleOuterClick = () => { @@ -75,44 +88,10 @@ const PopUp = ({ isOpen={menuIsOpen} > {({ getToggleButtonProps, getMenuProps, isOpen }) => ( -
- - -
{isOpen ? children : null}
-
+ + {renderTrigger(getToggleButtonProps())} + {isOpen ? children : null} + )} ); @@ -126,8 +105,16 @@ export const Share = ({ configKey, locale }: EmbedShareProps) => { }, [configKey, locale]); return ( Share} - triggerIconName="linkExternal" + renderTrigger={(props) => { + return ( + + ); + }} > <> @@ -230,8 +217,14 @@ export const Embed = ({ configKey, locale }: EmbedShareProps) => { return ( Embed} - triggerIconName="embed" + renderTrigger={(toggleProps) => ( + + )} > <> @@ -345,23 +338,6 @@ const CopyToClipboardTextInput = ({ iFrameCode }: { iFrameCode: string }) => { ); }; -// export const ImageDownload = () => { -// const handleClick = () => { -// console.log("download image"); -// }; -// return ( -// -// ); -// }; - -// Presentational Components - -// Modal const PublishActionModal = ({ children }: { children: ReactNode }) => ( { - const [state, dispatch] = useConfiguratorState(); - const locale = useLocale(); - const [{ data }] = useDataCubeMetadataWithComponentValuesQuery({ - variables: { iri: dataSetIri ?? "", locale }, - }); - - const goNext = useCallback(() => { - if (data?.dataCubeByIri) { - dispatch({ - type: "STEP_NEXT", - dataSetMetadata: data?.dataCubeByIri, - }); - } - }, [data, dispatch]); - - const goPrevious = useCallback(() => { - dispatch({ - type: "STEP_PREVIOUS", - }); - }, [dispatch]); - - const nextDisabled = - !canTransitionToNextStep(state, data?.dataCubeByIri) || - state.state === "PUBLISHING"; - const previousDisabled = - !canTransitionToPreviousStep(state) || state.state === "PUBLISHING"; - - const previousLabel = Previous; - const nextLabel = - state.state === "DESCRIBING_CHART" || state.state === "PUBLISHING" ? ( - Publish - ) : ( - Next - ); - - return ( - - {state.state === "SELECTING_CHART_TYPE" ? ( - - ) : ( - <> - - - - )} - - ); -}; - -const NextButton = ({ - label, - onClick, - disabled, -}: { - label: string | ReactNode; - onClick: () => void; - disabled: boolean; -}) => ( - -); diff --git a/app/configurator/components/configurator.tsx b/app/configurator/components/configurator.tsx index 2f89477b7..484f1576f 100644 --- a/app/configurator/components/configurator.tsx +++ b/app/configurator/components/configurator.tsx @@ -1,18 +1,11 @@ -import { Trans } from "@lingui/macro"; -import NextLink from "next/link"; import React from "react"; -import { Link } from "theme-ui"; -import { ConfiguratorState, useConfiguratorState } from ".."; +import { useConfiguratorState } from ".."; import { ChartPanel } from "../../components/chart-panel"; import { ChartPreview } from "../../components/chart-preview"; -import Stack from "../../components/Stack"; -import { useDataCubeMetadataQuery } from "../../graphql/query-hooks"; -import { useLocale } from "../../src"; import { ChartConfiguratorTable } from "../table/table-chart-configurator"; import { ChartAnnotationsSelector } from "./chart-annotations-selector"; import { ChartAnnotator } from "./chart-annotator"; import { ChartConfigurator } from "./chart-configurator"; -import { SectionTitle } from "./chart-controls/section"; import { ChartOptionsSelector } from "./chart-options-selector"; import { ChartTypeSelector } from "./chart-type-selector"; import { @@ -25,33 +18,6 @@ import { import { SelectDatasetStep } from "./select-dataset-step"; import { Stepper } from "./stepper"; -const DatasetSelector = ({ state }: { state: ConfiguratorState }) => { - const locale = useLocale(); - const [{ data: metaData }] = useDataCubeMetadataQuery({ - variables: { iri: state.dataSet || "", locale }, - pause: !state.dataSet, - }); - return ( -
- Dataset - {metaData ? ( - -
{metaData.dataCubeByIri?.title}
-
- - - - Choose another dataset - - - -
-
- ) : null} -
- ); -}; - const SelectChartTypeStep = () => { const [state] = useConfiguratorState(); if (state.state !== "SELECTING_CHART_TYPE") { @@ -60,10 +26,7 @@ const SelectChartTypeStep = () => { return ( <> - - - - + diff --git a/app/configurator/components/stepper.tsx b/app/configurator/components/stepper.tsx index c680c8105..a2760fa25 100644 --- a/app/configurator/components/stepper.tsx +++ b/app/configurator/components/stepper.tsx @@ -1,10 +1,16 @@ import { Trans } from "@lingui/macro"; -import { Fragment, ReactNode, useCallback, useMemo } from "react"; -import { Box, Button, Flex, Text } from "theme-ui"; -import { useConfiguratorState } from ".."; -import { Icon } from "../../icons"; -import { useTheme } from "../../themes"; -import { ActionBar } from "./action-bar"; +import React, { ReactNode, useCallback, useEffect } from "react"; +import { Button, ButtonProps, Flex, Text } from "theme-ui"; +import { + useConfiguratorState, + canTransitionToNextStep, + canTransitionToPreviousStep, +} from ".."; +import { useHeaderProgress } from "../../components/header"; +import { useDataCubeMetadataWithComponentValuesQuery } from "../../graphql/query-hooks"; +import SvgIcChevronLeft from "../../icons/components/IcChevronLeft"; +import SvgIcChevronRight from "../../icons/components/IcChevronRight"; +import { useLocale } from "../../src"; export type StepStatus = "past" | "current" | "future"; @@ -15,218 +21,216 @@ const steps = [ ] as const; type StepState = typeof steps[number]; -export const Stepper = ({ dataSetIri }: { dataSetIri?: string }) => { - const [{ state }, dispatch] = useConfiguratorState(); - - return useMemo(() => { - const currentStepIndex = steps.indexOf(state as $IntentionalAny); - return ( - - {/* Stepper container */} - - {steps.map((step, i) => ( - - i || state === "PUBLISHING" - ? "past" - : "future" - } - /> - {i < steps.length - 1 && ( - // Connection line - - )} - - ))} - +export const StepperDumb = ({ + goPrevious, + goNext, + state, + data, +}: { + goPrevious: () => void; + goNext: () => void; + state: ReturnType[0]; + data: ReturnType< + typeof useDataCubeMetadataWithComponentValuesQuery + >[0]["data"]; +}) => { + const nextDisabled = + !canTransitionToNextStep(state, data?.dataCubeByIri) || + state.state === "PUBLISHING"; + const previousDisabled = + !canTransitionToPreviousStep(state) || state.state === "PUBLISHING"; - - + const previousLabel = Back; + const nextLabel = + state.state === "DESCRIBING_CHART" || state.state === "PUBLISHING" ? ( + Publish this visualization + ) : ( + Next ); - }, [state, dataSetIri]); -}; -export const Step = ({ - stepState, - stepNumber, - status, -}: { - stepState: StepState; - stepNumber: number; - status: StepStatus; -}) => { - const theme = useTheme(); - const [{ dataSet }, dispatch] = useConfiguratorState(); + const currentStepIndex = steps.indexOf(state.state as $IntentionalAny); + const { value: progress, setValue: setProgress } = useHeaderProgress(); + useEffect(() => { + const run = async () => { + if ( + (currentStepIndex === 0 || currentStepIndex === -1) && + progress === 100 + ) { + setProgress(0); + await new Promise((resolve) => setTimeout(resolve, 100)); + } + setProgress( + Math.round(((currentStepIndex + 1) / (steps.length + 1)) * 100) + ); + }; + run(); + return () => { + setProgress(100); + }; + }, [currentStepIndex, setProgress, progress]); - const onClick = useCallback(() => { - if (status === "past" && dispatch) { - dispatch({ - type: "STEP_PREVIOUS", - to: stepState, - }); - } - }, [status, stepState, dispatch]); + return ( + + bg: "monochrome100", + borderBottomWidth: "1px", + borderBottomStyle: "solid", + borderBottomColor: "monochrome500", + overflow: "hidden", + }} + > + {/* Stepper container */} - {/* Icon */} - + + + {previousLabel} + + } + onClick={goPrevious} + disabled={previousDisabled} + variant="inline-bold" + /> + - bg: - status === "past" - ? "monochrome700" - : status === "current" - ? "brand" - : "monochrome600", - }} + - {status === "past" ? ( - - ) : ( - stepNumber - )} + + + + + {nextLabel}{" "} + {state.state === "DESCRIBING_CHART" ? null : ( + + )} + + } + onClick={goNext} + disabled={nextDisabled} + variant={ + state.state === "DESCRIBING_CHART" + ? "primary-small" + : "inline-bold" + } + /> - - - + ); +}; + +export const Stepper = ({ dataSetIri }: { dataSetIri?: string }) => { + const [state, dispatch] = useConfiguratorState(); + const locale = useLocale(); + const [{ data }] = useDataCubeMetadataWithComponentValuesQuery({ + variables: { iri: dataSetIri ?? "", locale }, + }); + const goNext = useCallback(() => { + if (data?.dataCubeByIri) { + dispatch({ + type: "STEP_NEXT", + dataSetMetadata: data?.dataCubeByIri, + }); + } + }, [data, dispatch]); + + const goPrevious = useCallback(() => { + dispatch({ + type: "STEP_PREVIOUS", + }); + }, [dispatch]); return ( - + ); }; -export const StepLabel = ({ - stepState, - highlight, -}: { - stepState: StepState; - highlight: boolean; -}) => { +export const CallToAction = ({ stepState }: { stepState: StepState }) => { switch (stepState) { case "SELECTING_CHART_TYPE": return ( - Visualization Type} - highlight={highlight} + + Select the desired chart type for your dataset. + + } /> ); case "CONFIGURING_CHART": return ( - Visualize} - highlight={highlight} + + Customize your visualization by using the filter and color + segmentation options in the sidebars. + + } /> ); case "DESCRIBING_CHART": return ( - Annotate} - highlight={highlight} + + Before publishing, add a title and description to your chart, and + choose which elements should be interactive. + + } /> ); } return null; }; -const StepLabelText = ({ - highlight, - label, -}: { - highlight: boolean; - label: ReactNode; -}) => { +const CallToActionText = ({ label }: { label: ReactNode }) => { return ( - <> - {/* Add background colored bold label underneath the actual - label, to avoid changing container's size when the text becomes bold. */} - - {label} - - {label} - - - + + {label} + ); }; + +const NavButton = ({ + label, + onClick, + disabled, + ...props +}: { + label: string | ReactNode; + onClick: () => void; + disabled: boolean; +} & ButtonProps) => ( + +); diff --git a/app/configurator/configurator-state.tsx b/app/configurator/configurator-state.tsx index 628338f17..f5fd24e71 100644 --- a/app/configurator/configurator-state.tsx +++ b/app/configurator/configurator-state.tsx @@ -965,7 +965,7 @@ const reducer: Reducer = ( } }; -const ConfiguratorStateContext = createContext< +export const ConfiguratorStateContext = createContext< [ConfiguratorState, Dispatch] | undefined >(undefined); diff --git a/app/docs/action-bar.docs.tsx b/app/docs/action-bar.docs.tsx deleted file mode 100644 index 306c69f34..000000000 --- a/app/docs/action-bar.docs.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { markdown, ReactSpecimen } from "catalog"; -import { Box, Text } from "theme-ui"; -import { ConfiguratorStateProvider } from "../configurator"; -import { ActionBar } from "../configurator/components/action-bar"; -import { states } from "./fixtures"; - -export default () => - markdown` -> The action bar is a container for components that trigger actions. - -## How to use - -~~~ -import { ActionBar } from "./components/action-bar" - - - {children} - -~~~ - -## Action Bar in different states - -${states.map((state) => ( - - {state.state} - - - - - - -))} -`; diff --git a/app/docs/button.docs.tsx b/app/docs/button.docs.tsx index 75e3c979b..58d7961da 100644 --- a/app/docs/button.docs.tsx +++ b/app/docs/button.docs.tsx @@ -1,20 +1,28 @@ import { Button } from "theme-ui"; import { markdown, ReactSpecimen } from "catalog"; +import { Icon } from "../icons"; +import SvgIcChevronRight from "../icons/components/IcChevronRight"; +import SvgIcChevronLeft from "../icons/components/IcChevronLeft"; export default () => markdown` > Buttons are used to trigger an event after a user interaction. -There are four basic styles that are styles defined in \`rebass\`'s \`variants\`: +There are four basic styles that are styles defined in \`theme-ui\`'s \`variants\`: - \`primary\` +- \`primary-small\` - \`secondary\` - \`success\` -- \`outline\` +- \`inline\` +- \`inline-bold\` ${( - + )} @@ -36,11 +44,37 @@ There are four basic styles that are styles defined in \`rebass\`'s \`variants\` )} + ${( + + + + )} + ${( + + + + )} + ${( + + + + )} ## How to use ~~~ import { Button } from "theme-ui" +import SvgIcChevronRight from "../icons/components/IcChevronRight"; +import SvgIcChevronLeft from "../icons/components/IcChevronLeft"; - - - - + + + + + + + +
diff --git a/app/themes/dark.ts b/app/themes/dark.ts deleted file mode 100644 index 0820cb58c..000000000 --- a/app/themes/dark.ts +++ /dev/null @@ -1,373 +0,0 @@ -/** - * IMPORTANT: just export JSON-serializable data from this file! - * - * It will be loaded in _app.tsx's `getInitialProps()`, which will serialize to JSON. - * So references to other modules, functions etc. won't work here. - * - * - `theme` should be a plain object, conforming to the `Theme` type. - */ -import { Theme } from "./index"; - -/** - * Theme conforming to the Swiss Federal CD guidelines - */ -export const theme: Theme = { - breakpoints: ["48em", "62em", "75em"], - space: [ - "0", - "0.25rem", - "0.5rem", - "0.75rem", - "1rem", - "1.5rem", - "2rem", - "4rem", - "4.5rem", - ], - colors: { - text: "monochrome100", - background: "monochrome900", - - brand: "#DC0018", - monochrome100: "#000000", - monochrome200: "#333333", - monochrome300: "#454545", - monochrome400: "#757575", - monochrome500: "#CCCCCC", - monochrome600: "#D5D5D5", - monochrome700: "#E5E5E5", - monochrome800: "#F5F5F5", - monochrome900: "#FFFFFF", - - primary: "#006699", - primaryHover: "#004B70", - primaryActive: "#00334D", - primaryDisabled: "#599cbd", - primaryLight: "#d8e8ef", - - secondary: "#757575", - secondaryHover: "#616161", - secondaryActive: "#454545", - secondaryDisabled: "#a6a6a6", - - success: "#3c763d", - successHover: "#3c763d", - successActive: "#3c763d", - successDisabled: "#DFF0D8", - successLight: "#DFF0D8", - - muted: "#616171", - mutedColored: "#454556", - mutedDarker: "#252536", - - focus: "hotpink", - error: "#FF5555", - hint: "#757575", - missing: "#EFEFEF", - - alert: "#DC0018", - alertLight: "#ffe6e1", - }, - fonts: { - body: - "FrutigerNeue, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol", - monospace: "Menlo, monospace", - }, - fontSizes: [ - "0rem", - "0.625rem", - "0.75rem", - "0.875rem", - "1rem", - "1.125rem", - "1.5rem", - "2rem", - "2.5rem", - "3rem", - "4.5rem", - "5.5rem", - ], - fontWeights: { - light: 300, - regular: 400, - heading: 700, - bold: 700, - }, - // FIXME: should it be relative values? 1.5, etc. - lineHeights: [ - "0rem", - "1rem", - "1.125rem", - "1.25rem", - "1.375rem", - "1.5rem", - "1.750rem", - "2.250rem", - "3rem", - "4rem", - "4.5rem", - ], - radii: { - default: 3, - bigger: 4, - circle: 99999, - }, - shadows: { - primary: "0 3px 5px 0 rgba(255,255,255,0.10)", - rightSide: "2px 0 4px 0 rgba(255,255,255,0.05)", - leftSide: "-2px 0 2px 0 rgba(255,255,255,0.05)", - tooltip: "0 2px 8px rgba(255, 255, 255, 0.25)", - }, - text: { - giga: { - fontFamily: "body", - lineHeight: [9, 10, 10], - fontWeight: "light", - fontSize: [8, 9, 9], - }, - heading1: { - fontFamily: "body", - lineHeight: [7, 8, 8], - fontWeight: "bold", - fontSize: [6, 7, 7], - }, - heading2: { - fontFamily: "body", - lineHeight: [6, 7, 7], - fontWeight: "regular", - fontSize: [5, 6, 6], - }, - heading3: { - fontFamily: "body", - lineHeight: [5, 6, 6], - fontWeight: "bold", - fontSize: [4, 5, 5], - }, - lead: { - fontFamily: "body", - lineHeight: [4, 5, 5], - fontWeight: "bold", - fontSize: [3, 4, 4], - }, - paragraph1: { - fontFamily: "body", - lineHeight: [4, 5, 5], - fontWeight: "regular", - fontSize: [3, 4, 4], - }, - paragraph2: { - fontFamily: "body", - lineHeight: [2, 4, 3], - fontWeight: "regular", - fontSize: [2, 3, 3], - }, - table: { - fontFamily: "body", - lineHeight: [2, 4, 4], - fontWeight: "regular", - fontSize: [2, 3, 3], - }, - meta: { - fontFamily: "body", - lineHeight: [1, 2, 2], - fontWeight: "regular", - fontSize: [1, 2, 2], - }, - }, - styles: { - // Overwrite default browser styles. - root: { - // "root" applies to "body" - // @ts-ignore - "@font-face": [ - { - fontFamily: "FrutigerNeue", - fontStyle: "normal", - fontWeight: 700, - src: `url("/static/fonts/FrutigerNeueW02-Bd.woff2") format("woff2"), - url("/static/fonts/FrutigerNeueW02-Bd.woff") format("woff")`, - }, - { - fontFamily: "FrutigerNeue", - fontStyle: "normal", - fontWeight: 400, - src: `url("/static/fonts/FrutigerNeueW02-Regular.woff2") format("woff2"), - url("/static/fonts/FrutigerNeueW02-Regular.woff") format("woff")`, - }, - { - fontFamily: "FrutigerNeue", - fontStyle: "normal", - fontWeight: 300, - src: `url("/static/fonts/FrutigerNeueW02-Light.woff2") format("woff2"), - url("/static/fonts/FrutigerNeueW02-Light.woff") format("woff")`, - }, - { - fontFamily: "FrutigerNeue", - fontStyle: "italic", - fontWeight: 400, - src: `url("/static/fonts/FrutigerNeueW02-It.woff2") format("woff2"), - url("/static/fonts/FrutigerNeueW02-It.woff") format("woff")`, - }, - ], - - bg: "monochrome100", - margin: 0, - padding: 0, - fontFamily: - "FrutigerNeue, -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol", - - // Hack around type error for vendor prefixed rules - ...{ - // Use momentum-based scrolling on iOS devices - WebkitOverflowScrolling: "touch", - - // Auto-hide scrollbars in Edge - msOverflowStyle: "-ms-autohiding-scrollbar", - }, - - svg: { - display: "block", - }, - - "*:focus": { - outline: "3px solid #ddd", - }, - - fieldset: { - border: 0, - padding: "0.01em 0 0 0", - margin: 0, - minWidth: 0, - }, - }, - }, - buttons: { - reset: { - background: "transparent", - border: "none", - }, - primary: { - bg: "primary", - color: "monochrome100", - borderRadius: "default", - width: ["100%", "auto"], - minWidth: "160px", - px: 7, - py: 3, - fontFamily: "body", - fontSize: 4, - transition: "background-color .2s", - cursor: "pointer", - ":hover": { - bg: "primaryHover", - }, - ":active": { - bg: "primaryHover", - }, - ":disabled": { - cursor: "initial", - bg: "primaryDisabled", - }, - }, - success: { - variant: "buttons.primary", - bg: "successBase", - ":hover": { - bg: "successHover", - }, - ":active": { - bg: "successHover", - }, - ":disabled": { - cursor: "initial", - bg: "successDisabled", - }, - }, - outline: { - variant: "buttons.primary", - color: "primary", - bg: "monochrome100", - border: "1px", - borderWidth: "1px", - borderStyle: "solid", - borderColor: "primary", - ":hover": { - bg: "muted", - }, - ":active": { - bg: "muted", - }, - ":disabled": { - cursor: "initial", - bg: "muted", - }, - }, - secondary: { - variant: "buttons.primary", - bg: "secondary", - ":hover": { - bg: "secondaryHover", - }, - ":active": { - bg: "secondaryHover", - }, - ":disabled": { - cursor: "initial", - bg: "secondaryDisabled", - }, - }, - inverted: { - bg: "monochrome100", - color: "monochrome800", - borderRadius: "default", - width: ["100%", "auto"], - minWidth: 160, - px: 7, - py: 3, - fontFamily: "body", - fontSize: 4, - transition: "background-color .2s", - cursor: "pointer", - ":hover": { - bg: "monochrome300", - }, - ":active": { - bg: "monochrome400", - }, - ":disabled": { - cursor: "initial", - color: "monochrome600", - bg: "monochrome300", - }, - }, - inline: { - background: "transparent", - color: "primary", - fontFamily: "body", - lineHeight: [1, 2, 2], - fontWeight: "regular", - fontSize: [3, 3, 3], - border: "none", - cursor: "pointer", - mb: 4, - p: 0, - ":hover": { - color: "primaryHover", - }, - ":disabled": { - cursor: "initial", - color: "monochrome500", - }, - }, - }, -}; - -/** - * Load these fonts early using - * Use WOFF2 fonts if possible! - */ -export const preloadFonts = [ - "/static/fonts/FrutigerNeueW02-Bd.woff2", - "/static/fonts/FrutigerNeueW02-Regular.woff2", - "/static/fonts/FrutigerNeueW02-Light.woff2", -]; diff --git a/app/themes/federal.ts b/app/themes/federal.ts index f168ec2a0..9f19b59f8 100644 --- a/app/themes/federal.ts +++ b/app/themes/federal.ts @@ -50,8 +50,9 @@ export const theme: Theme = { secondaryActive: "#454545", secondaryDisabled: "#a6a6a6", - secondaryButton: "#d8e8ef", - secondaryButtonHover: "#CCDFE7", + secondaryButton: "#757575", + secondaryButtonText: "white", + secondaryButtonHover: "#646464", success: "#3c763d", successHover: "#3c763d", @@ -120,6 +121,7 @@ export const theme: Theme = { radii: { default: 3, bigger: 4, + xl: 25, circle: 99999, }, shadows: { @@ -256,18 +258,34 @@ export const theme: Theme = { background: "transparent", border: "none", }, - primary: { - bg: "primary", - color: "monochrome100", - borderRadius: "default", - width: ["100%", "auto"], - minWidth: "160px", + base: { px: 4, py: 3, fontFamily: "body", fontSize: 4, + borderRadius: "default", transition: "background-color .2s", cursor: "pointer", + display: "inline-flex", + alignItems: "center", + flexGrow: 0, + justifyContent: "center", + "& > svg": { + width: 22, + mt: -1, + mb: -1, + }, + "& > svg:first-child": { + marginRight: 2, + }, + "& > svg:last-child": { + marginLeft: 2, + }, + }, + primary: { + variant: "buttons.base", + bg: "primary", + color: "monochrome100", ":hover": { bg: "primaryHover", }, @@ -279,8 +297,23 @@ export const theme: Theme = { bg: "primaryDisabled", }, }, + "primary-small": { + variant: "buttons.base", + fontSize: 3, + fontWeight: "normal", + py: 2, + minWidth: "auto", + "& > svg:first-child": { + width: "auto", + marginRight: 1, + }, + "& > svg:last-child": { + width: "auto", + marginLeft: 1, + }, + }, success: { - variant: "buttons.primary", + variant: "buttons.base", bg: "successBase", ":hover": { bg: "successHover", @@ -294,7 +327,7 @@ export const theme: Theme = { }, }, outline: { - variant: "buttons.primary", + variant: "buttons.base", color: "primary", bg: "monochrome100", border: "1px", @@ -315,7 +348,7 @@ export const theme: Theme = { secondary: { variant: "buttons.primary", bg: "secondaryButton", - color: "primary", + color: "secondaryButtonText", ":hover": { bg: "secondaryButtonHover", }, @@ -328,11 +361,10 @@ export const theme: Theme = { }, }, inverted: { + variant: "buttons.base", bg: "monochrome100", color: "monochrome800", borderRadius: "default", - width: ["100%", "auto"], - minWidth: 160, px: 4, py: 3, fontFamily: "body", @@ -352,6 +384,7 @@ export const theme: Theme = { }, }, inline: { + variant: "buttons.base", background: "transparent", color: "primary", fontFamily: "body", @@ -369,6 +402,42 @@ export const theme: Theme = { cursor: "initial", color: "monochrome500", }, + "& > svg:first-child": { + width: "auto", + marginRight: 1, + }, + "& > svg:last-child": { + width: "auto", + marginLeft: 1, + }, + }, + "inline-bold": { + variant: "buttons.base", + background: "transparent", + color: "monochrome800", + fontFamily: "body", + lineHeight: [1, 2, 2], + fontWeight: "bold", + fontSize: [3, 3, 3], + border: "none", + cursor: "pointer", + m: 0, + p: 0, + ":hover": { + color: "primaryHover", + }, + ":disabled": { + cursor: "initial", + color: "monochrome500", + }, + "& > svg:first-child": { + width: "auto", + marginRight: 1, + }, + "& > svg:last-child": { + width: "auto", + marginLeft: 1, + }, }, selectColorPicker: { color: "monochrome700",