diff --git a/.gitignore b/.gitignore index dff8f8c..1357ce5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Project -hyperglass/hyperglass/static +static/ TODO* .env diff --git a/hyperglass/configuration/validate.py b/hyperglass/configuration/validate.py index 3accb7e..f5868eb 100644 --- a/hyperglass/configuration/validate.py +++ b/hyperglass/configuration/validate.py @@ -125,6 +125,7 @@ def init_ui_params(*, params: "Params", devices: "Devices") -> "UIParameters": _ui_params = params.frontend() _ui_params["web"]["logo"]["light_format"] = params.web.logo.light.suffix _ui_params["web"]["logo"]["dark_format"] = params.web.logo.dark.suffix + _ui_params["web"]["logo"]["footer_format"] = params.web.logo.footer.suffix return UIParameters( **_ui_params, diff --git a/hyperglass/frontend/__init__.py b/hyperglass/frontend/__init__.py index bbd7ace..c2a24e3 100644 --- a/hyperglass/frontend/__init__.py +++ b/hyperglass/frontend/__init__.py @@ -203,7 +203,7 @@ def migrate_images(app_path: Path, params: "UIParameters"): src_files = () dst_files = () - for image in ("light", "dark", "favicon"): + for image in ("light", "dark", "favicon", "footer"): src: Path = getattr(params.web.logo, image) dst = images_dir / f"{image + src.suffix}" src_files += (src,) diff --git a/hyperglass/models/config/params.py b/hyperglass/models/config/params.py index 1e35ba2..1ced6f3 100644 --- a/hyperglass/models/config/params.py +++ b/hyperglass/models/config/params.py @@ -47,12 +47,12 @@ class ParamsPublic(HyperglassModel): description="Your network's primary ASN. This field is used to set some useful defaults such as the subtitle and PeeringDB URL.", ) org_name: str = Field( - "Beloved Hyperglass User", + "Beloved Looking Glass User", title="Organization Name", description="Your organization's name. This field is used in the UI & API documentation to set fields such as `` HTML tags for SEO and the terms & conditions footer component.", ) site_title: str = Field( - "hyperglass", + "Looking Glass", title="Site Title", description="The name of your hyperglass site. This field is used in the UI & API documentation to set fields such as the `` HTML tag, and the terms & conditions footer component.", ) @@ -131,8 +131,8 @@ class Params(ParamsPublic, HyperglassModel): for menu in web.menus: menu.content = menu.content.format( - site_title=info.data.get("site_title", "hyperglass"), - org_name=info.data.get("org_name", "hyperglass"), + site_title=info.data.get("site_title", "Looking Glass"), + org_name=info.data.get("org_name", "Looking Glass"), version=__version__, ) return web diff --git a/hyperglass/models/config/web.py b/hyperglass/models/config/web.py index 7916d9e..62b24d2 100644 --- a/hyperglass/models/config/web.py +++ b/hyperglass/models/config/web.py @@ -86,6 +86,7 @@ class Logo(HyperglassModel): light: FilePath = DEFAULT_IMAGES / "hyperglass-light.svg" dark: FilePath = DEFAULT_IMAGES / "hyperglass-dark.svg" favicon: FilePath = DEFAULT_IMAGES / "hyperglass-icon.svg" + footer: FilePath = DEFAULT_IMAGES / "hyperglass-icon.svg" width: str = Field(default="50%", pattern=PERCENTAGE_PATTERN) height: t.Optional[str] = Field(default=None, pattern=PERCENTAGE_PATTERN) @@ -95,13 +96,13 @@ class LogoPublic(Logo): light_format: str dark_format: str - + footer_format: str class Text(HyperglassModel): """Validation model for params.branding.text.""" title_mode: TitleMode = "logo_only" - title: str = Field(default="hyperglass", max_length=32) + title: str = Field(default="Looking Glass", max_length=32) subtitle: str = Field(default="Network Looking Glass", max_length=32) query_location: str = "Location" query_type: str = "Query Type" @@ -132,7 +133,7 @@ class Text(HyperglassModel): class ThemeColors(HyperglassModel): """Validation model for theme colors.""" - black: Color = "#000000" + black: Color = "#2d3635" white: Color = "#ffffff" dark: Color = "#010101" light: Color = "#f5f6f7" @@ -169,7 +170,7 @@ class ThemeColors(HyperglassModel): class ThemeFonts(HyperglassModel): """Validation model for theme fonts.""" - body: str = "Nunito" + body: str = "Inter" mono: str = "Fira Code" @@ -226,6 +227,7 @@ class HighlightPattern(HyperglassModel): class Web(HyperglassModel): """Validation model for all web/browser-related configuration.""" + copyright: t.Optional[str] = "All rights reserved" credit: Credit = Credit() dns_provider: DnsOverHttps = DnsOverHttps() links: t.Sequence[Link] = [ diff --git a/hyperglass/ui/components/footer/button.tsx b/hyperglass/ui/components/footer/button.tsx index 3546849..ec59765 100644 --- a/hyperglass/ui/components/footer/button.tsx +++ b/hyperglass/ui/components/footer/button.tsx @@ -2,13 +2,12 @@ import { useMemo } from 'react'; import { Button, Menu, MenuButton, MenuList } from '@chakra-ui/react'; import { useConfig } from '~/context'; import { Markdown } from '~/elements'; -import { useColorValue, useBreakpointValue, useOpposingColor, useStrf } from '~/hooks'; +import { useStrf } from '~/hooks'; import type { MenuListProps } from '@chakra-ui/react'; import type { Config } from '~/types'; interface FooterButtonProps extends Omit<MenuListProps, 'title'> { - side: 'left' | 'right'; title?: MenuListProps['children']; content: string; } @@ -27,26 +26,26 @@ function getConfigFmt(config: Config): Record<string, string> { } export const FooterButton = (props: FooterButtonProps): JSX.Element => { - const { content, title, side, ...rest } = props; + const { content, title, ...rest } = props; const config = useConfig(); const strF = useStrf(); const fmt = useMemo(() => getConfigFmt(config), [config]); const fmtContent = useMemo(() => strF(content, fmt), [fmt, content, strF]); - const placement = side === 'left' ? 'top' : side === 'right' ? 'top-end' : undefined; - const bg = useColorValue('white', 'gray.900'); - const color = useOpposingColor(bg); - const size = useBreakpointValue({ base: 'xs', lg: 'sm' }); - return ( - <Menu placement={placement} preventOverflow isLazy> + <Menu + placement="top" + preventOverflow + isLazy + > <MenuButton zIndex={2} + py={1} + fontWeight="normal" as={Button} - size={size} - variant="ghost" - lineHeight={0} + variant="link" + colorScheme="transparent" aria-label={typeof title === 'string' ? title : undefined} > {title} @@ -54,10 +53,10 @@ export const FooterButton = (props: FooterButtonProps): JSX.Element => { <MenuList px={6} py={4} - bg={bg} + bg="white" // Ensure the height doesn't overtake the viewport, especially on mobile. See overflow also. maxH="50vh" - color={color} + color="black" boxShadow="2xl" textAlign="left" overflowY="auto" diff --git a/hyperglass/ui/components/footer/color-mode.tsx b/hyperglass/ui/components/footer/color-mode.tsx deleted file mode 100644 index 5059b97..0000000 --- a/hyperglass/ui/components/footer/color-mode.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { forwardRef } from 'react'; -import { Button, Tooltip } from '@chakra-ui/react'; -import { Switch, Case } from 'react-if'; -import { DynamicIcon } from '~/elements'; -import { useOpposingColor, useColorMode, useColorValue, useBreakpointValue } from '~/hooks'; - -import type { ButtonProps } from '@chakra-ui/react'; - -interface ColorModeToggleProps extends Omit<ButtonProps, 'size'> { - size?: string | number; -} - -export const ColorModeToggle = forwardRef<HTMLButtonElement, ColorModeToggleProps>( - (props: ColorModeToggleProps, ref) => { - const { size = '1.5rem', ...rest } = props; - const { colorMode, toggleColorMode } = useColorMode(); - - const bg = useColorValue('primary.500', 'yellow.300'); - const color = useOpposingColor(bg); - const label = useColorValue('Switch to dark mode', 'Switch to light mode'); - const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' }); - - return ( - <Tooltip hasArrow placement="top-end" label={label} bg={bg} color={color}> - <Button - ref={ref} - size={btnSize} - title={label} - variant="ghost" - aria-label={label} - _hover={{ color: bg }} - color="currentColor" - onClick={toggleColorMode} - {...rest} - > - <Switch> - <Case condition={colorMode === 'light'}> - <DynamicIcon icon={{ hi: 'HiMoon' }} boxSize={size} /> - </Case> - <Case condition={colorMode === 'dark'}> - <DynamicIcon icon={{ hi: 'HiSun' }} boxSize={size} /> - </Case> - </Switch> - </Button> - </Tooltip> - ); - }, -); diff --git a/hyperglass/ui/components/footer/footer.tsx b/hyperglass/ui/components/footer/footer.tsx index 17d3fea..811b78c 100644 --- a/hyperglass/ui/components/footer/footer.tsx +++ b/hyperglass/ui/components/footer/footer.tsx @@ -1,15 +1,15 @@ -import { Flex, HStack, useToken } from '@chakra-ui/react'; +import { Box, Container, Flex, Grid, GridItem } from '@chakra-ui/react'; import { useMemo } from 'react'; import { useConfig } from '~/context'; -import { DynamicIcon } from '~/elements'; -import { useBreakpointValue, useColorValue, useMobile } from '~/hooks'; +import { DynamicIcon, Markdown } from '~/elements'; +import { useMobile } from '~/hooks'; import { isLink, isMenu } from '~/types'; import { FooterButton } from './button'; -import { ColorModeToggle } from './color-mode'; import { FooterLink } from './link'; import type { ButtonProps, LinkProps } from '@chakra-ui/react'; import type { Link, Menu } from '~/types'; +import { Logo } from './logo'; type MenuItems = (Link | Menu)[]; @@ -24,7 +24,7 @@ function buildItems(links: Link[], menus: Menu[]): [MenuItems, MenuItems] { return [left, right]; } -const LinkOnSide = (props: { item: ArrayElement<MenuItems>; side: 'left' | 'right' }) => { +const NavLink = (props: { item: ArrayElement<MenuItems> }) => { const { item, side } = props; if (isLink(item)) { const icon: Partial<ButtonProps & LinkProps> = {}; @@ -40,49 +40,55 @@ const LinkOnSide = (props: { item: ArrayElement<MenuItems>; side: 'left' | 'righ }; export const Footer = (): JSX.Element => { - const { web, content } = useConfig(); - - const footerBg = useColorValue('blackAlpha.50', 'whiteAlpha.100'); - const footerColor = useColorValue('black', 'white'); - - const size = useBreakpointValue({ base: useToken('sizes', 4), lg: useToken('sizes', 6) }); - - const isMobile = useMobile(); + const { web } = useConfig(); const [left, right] = useMemo(() => buildItems(web.links, web.menus), [web.links, web.menus]); return ( - <HStack - px={6} - py={4} + <Box w="100%" - zIndex={1} - as="footer" - bg={footerBg} - whiteSpace="nowrap" - color={footerColor} - spacing={{ base: 8, lg: 6 }} - display={{ base: 'inline-block', lg: 'flex' }} - overflowY={{ base: 'auto', lg: 'unset' }} - justifyContent={{ base: 'center', lg: 'space-between' }} + bg="#005e8a" + color="#ffffff" + fontSize=".945rem" > - {left.map(item => ( - <LinkOnSide key={item.title} item={item} side="left" /> - ))} - {!isMobile && <Flex p={0} flex="1 0 auto" maxWidth="100%" mr="auto" />} - {right.map(item => ( - <LinkOnSide key={item.title} item={item} side="right" /> - ))} - {web.credit.enable && ( - <FooterButton - key="credit" - side="right" - content={content.credit} - title={<DynamicIcon icon={{ fi: 'FiCode' }} boxSize={size} />} - /> - )} - - <ColorModeToggle size={size} /> - </HStack> + <Container maxW="8xl"> + <Grid + px={6} + py={4} + w="100%" + zIndex={1} + as="footer" + whiteSpace="nowrap" + templateColumns="repeat(4, 1fr)" + gap={6} + > + <GridItem colSpan={{base: 4, md: 2}}> + <Flex + flex="1 0 auto" + key="credit" + side="left" + maxW="50%" + > + <Box w="100%" maxW="15vw" my="2" mr="4"> + <Logo /> + </Box> + <Markdown content={web.copyright} /> + </Flex> + </GridItem> + <GridItem colSpan={{base: 4, md: 1}}> + <Flex flexDirection="column" alignItems="start"> + {left.map(item => ( + <NavLink key={item.title} item={item} /> + ))} + </Flex> + </GridItem> + <GridItem colSpan={{base: 4, md: 1}}> + {right.map(item => ( + <NavLink key={item.title} item={item} /> + ))} + </GridItem> + </Grid> + </Container> + </Box> ); }; diff --git a/hyperglass/ui/components/footer/link.tsx b/hyperglass/ui/components/footer/link.tsx index cac171e..cc32975 100644 --- a/hyperglass/ui/components/footer/link.tsx +++ b/hyperglass/ui/components/footer/link.tsx @@ -1,5 +1,4 @@ import { Button, Link } from '@chakra-ui/react'; -import { useBreakpointValue } from '~/hooks'; import type { ButtonProps, LinkProps } from '@chakra-ui/react'; @@ -7,9 +6,17 @@ type FooterLinkProps = ButtonProps & LinkProps & { title: string }; export const FooterLink = (props: FooterLinkProps): JSX.Element => { const { title } = props; - const btnSize = useBreakpointValue({ base: 'xs', lg: 'sm' }); return ( - <Button as={Link} isExternal size={btnSize} variant="ghost" aria-label={title} {...props}> + <Button + as={Link} + isExternal + py={1} + fontWeight="normal" + variant="link" + colorScheme="transparent" + aria-label={title} + {...props} + > {title} </Button> ); diff --git a/hyperglass/ui/components/footer/logo.tsx b/hyperglass/ui/components/footer/logo.tsx new file mode 100644 index 0000000..0a070e1 --- /dev/null +++ b/hyperglass/ui/components/footer/logo.tsx @@ -0,0 +1,37 @@ +import { Image, Skeleton } from '@chakra-ui/react'; +import { useConfig } from '~/context'; + +import type { ImageProps } from '@chakra-ui/react'; + +export const Logo = (props: ImageProps): JSX.Element => { + const { web } = useConfig(); + const { footerFormat } = web.logo; + + return ( + <Image + src={`/images/footer${footerFormat}`} + alt={web.text.title} + width="100%" + css={{ + objectFit: 'contain', + objectPosition: 'top', + userDrag: 'none', + userSelect: 'none', + msUserSelect: 'none', + MozUserSelect: 'none', + WebkitUserDrag: 'none', + WebkitUserSelect: 'none', + }} + fallback={ + <Skeleton + isLoaded={false} + borderRadius="md" + endColor="light.500" + startColor="whiteSolid.100" + width="100%" + /> + } + {...props} + /> + ); +}; diff --git a/hyperglass/ui/components/form-field.tsx b/hyperglass/ui/components/form-field.tsx index 63f02e0..d987491 100644 --- a/hyperglass/ui/components/form-field.tsx +++ b/hyperglass/ui/components/form-field.tsx @@ -2,7 +2,7 @@ import { Flex, FormControl, FormErrorMessage, FormLabel } from '@chakra-ui/react import { useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; import { If, Then } from 'react-if'; -import { useBooleanValue, useColorValue } from '~/hooks'; +import { useBooleanValue } from '~/hooks'; import type { FormControlProps } from '@chakra-ui/react'; import type { FieldError } from 'react-hook-form'; @@ -18,8 +18,8 @@ interface FormFieldProps extends FormControlProps { export const FormField = (props: FormFieldProps): JSX.Element => { const { name, label, children, labelAddOn, fieldAddOn, hiddenLabels = false, ...rest } = props; - const labelColor = useColorValue('blackAlpha.700', 'whiteAlpha.700'); - const errorColor = useColorValue('red.500', 'red.300'); + const labelColor = 'blackAlpha.700'; + const errorColor = 'red.500'; const opacity = useBooleanValue(hiddenLabels, 0, undefined); const { formState } = useFormContext<FormData>(); @@ -47,7 +47,6 @@ export const FormField = (props: FormFieldProps): JSX.Element => { > <FormLabel pr={0} - mb={{ lg: 4 }} htmlFor={name} display="flex" opacity={opacity} diff --git a/hyperglass/ui/components/greeting.tsx b/hyperglass/ui/components/greeting.tsx index 7ee576a..fe85b7c 100644 --- a/hyperglass/ui/components/greeting.tsx +++ b/hyperglass/ui/components/greeting.tsx @@ -12,7 +12,7 @@ import { import { If, Then } from 'react-if'; import { Markdown } from '~/elements'; import { useConfig } from '~/context'; -import { useGreeting, useColorValue, useOpposingColor } from '~/hooks'; +import { useGreeting } from '~/hooks'; import type { ModalContentProps } from '@chakra-ui/react'; @@ -20,9 +20,6 @@ export const Greeting = (props: ModalContentProps): JSX.Element => { const { web, content } = useConfig(); const { isAck, isOpen, open, ack } = useGreeting(); - const bg = useColorValue('white', 'gray.800'); - const color = useOpposingColor(bg); - useEffect(() => { if (!web.greeting.enable && !web.greeting.required) { ack(true, false); @@ -44,8 +41,8 @@ export const Greeting = (props: ModalContentProps): JSX.Element => { <ModalOverlay /> <ModalContent py={4} - bg={bg} - color={color} + bg="white" + color="black" borderRadius="md" maxW={{ base: '95%', md: '75%' }} {...props} diff --git a/hyperglass/ui/components/header/header.tsx b/hyperglass/ui/components/header/header.tsx index b36470a..b9f2241 100644 --- a/hyperglass/ui/components/header/header.tsx +++ b/hyperglass/ui/components/header/header.tsx @@ -1,11 +1,9 @@ -import { Flex, ScaleFade } from '@chakra-ui/react'; +import { Container, Flex, ScaleFade } from '@chakra-ui/react'; import { motionChakra } from '~/elements'; import { useBooleanValue, useBreakpointValue, useFormInteractive } from '~/hooks'; import { Title } from './title'; -const Wrapper = motionChakra('header', { - baseStyle: { display: 'flex', px: 4, pt: 6, minH: 16, w: 'full', flex: '0 1 auto' }, -}); +const Wrapper = motionChakra('header'); export const Header = (): JSX.Element => { const formInteractive = useFormInteractive(); @@ -18,17 +16,26 @@ export const Header = (): JSX.Element => { return ( <Wrapper layout="position"> - <ScaleFade in initialScale={0.5} style={{ width: '100%' }}> - <Flex - height="100%" - maxW={titleWidth} - // This is here for the logo - justifyContent="center" - mx="auto" - > - <Title /> - </Flex> - </ScaleFade> + <Container + maxW="8xl" + display="flex" + px={4} + pt={6} + minH={16} + flex="0 1 auto" + > + <ScaleFade in initialScale={0.5} style={{ width: '100%' }}> + <Flex + height="100%" + maxW={titleWidth} + // This is here for the logo + justifyContent="center" + mx="auto" + > + <Title /> + </Flex> + </ScaleFade> + </Container> </Wrapper> ); }; diff --git a/hyperglass/ui/components/header/logo.tsx b/hyperglass/ui/components/header/logo.tsx index a5aa6ec..89fabf0 100644 --- a/hyperglass/ui/components/header/logo.tsx +++ b/hyperglass/ui/components/header/logo.tsx @@ -1,46 +1,17 @@ import { Image, Skeleton } from '@chakra-ui/react'; -import { useCallback, useMemo, useState } from 'react'; import { useConfig } from '~/context'; import { useColorValue } from '~/hooks'; import type { ImageProps } from '@chakra-ui/react'; -/** - * Custom hook to handle loading the user's logo, errors loading the logo, and color mode changes. - */ -function useLogo(): [string, () => void] { - const { web } = useConfig(); - const { darkFormat, lightFormat } = web.logo; - - const src = useColorValue(`/images/light${darkFormat}`, `/images/dark${lightFormat}`); - - // Use the hyperglass logo if the user's logo can't be loaded for whatever reason. - const [fallback, setSource] = useState<string | null>(null); - - // If the user image cannot be loaded, log an error to the console and set the fallback image. - const setFallback = useCallback(() => { - console.warn(`Error loading image from '${src}'`); - setSource('https://res.cloudinary.com/hyperglass/image/upload/v1593916013/logo-light.svg'); - }, [src]); - - // Only return the fallback image if it's been set. - return useMemo(() => [fallback ?? src, setFallback], [fallback, setFallback, src]); -} - export const Logo = (props: ImageProps): JSX.Element => { const { web } = useConfig(); - const { width } = web.logo; - - const skeletonA = useColorValue('whiteSolid.100', 'blackSolid.800'); - const skeletonB = useColorValue('light.500', 'dark.500'); - - const [source, setFallback] = useLogo(); + const { lightFormat, width } = web.logo; return ( <Image - src={source} + src={`/images/light${lightFormat}`} alt={web.text.title} - onError={setFallback} maxW={{ base: '100%', md: width }} width="auto" css={{ @@ -55,8 +26,8 @@ export const Logo = (props: ImageProps): JSX.Element => { <Skeleton isLoaded={false} borderRadius="md" - endColor={skeletonB} - startColor={skeletonA} + endColor="light.500" + startColor="whiteSolid.100" width={{ base: 64, lg: 80 }} height={{ base: 12, lg: 16 }} /> diff --git a/hyperglass/ui/components/layout.tsx b/hyperglass/ui/components/layout.tsx index 585a73a..30827f7 100644 --- a/hyperglass/ui/components/layout.tsx +++ b/hyperglass/ui/components/layout.tsx @@ -1,4 +1,4 @@ -import { Flex } from '@chakra-ui/react'; +import { Container, Flex } from '@chakra-ui/react'; import { useCallback, useRef } from 'react'; import { isSafari } from 'react-device-detect'; import { If, Then } from 'react-if'; @@ -53,15 +53,17 @@ export const Layout = (props: FlexProps): JSX.Element => { minHeight={isSafari ? '-webkit-fill-available' : '100vh'} > <Header /> - <Main - layout - animate={{ opacity: 1, y: 0 }} - transition={{ duration: 0.3 }} - exit={{ opacity: 0, x: -300 }} - initial={{ opacity: 0, y: 300 }} - > - {props.children} - </Main> + <Container maxW="8xl" display="flex" flex="1 1 auto"> + <Main + layout + animate={{ opacity: 1, y: 0 }} + transition={{ duration: 0.3 }} + exit={{ opacity: 0, x: -300 }} + initial={{ opacity: 0, y: 300 }} + > + {props.children} + </Main> + </Container> <Footer /> <If condition={developerMode}> <Then> diff --git a/hyperglass/ui/components/location-card.tsx b/hyperglass/ui/components/location-card.tsx index 6797ac9..ec32cbd 100644 --- a/hyperglass/ui/components/location-card.tsx +++ b/hyperglass/ui/components/location-card.tsx @@ -1,7 +1,6 @@ import { useMemo, useState } from 'react'; import { Flex, Avatar, chakra } from '@chakra-ui/react'; import { motionChakra } from '~/elements'; -import { useColorValue, useOpposingColor } from '~/hooks'; import type { SingleOption } from '~/types'; import type { LocationOption } from './query-location'; @@ -43,11 +42,9 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => { } } - const bg = useColorValue('white', 'blackSolid.600'); - const imageBorder = useColorValue('gray.600', 'whiteAlpha.800'); - const fg = useOpposingColor(bg); - const checkedBorder = useColorValue('blue.400', 'blue.300'); - const errorBorder = useColorValue('red.500', 'red.300'); + const imageBorder = 'gray.600'; + const checkedBorder = 'brand.500'; + const errorBorder = 'red.500'; const borderColor = useMemo( () => @@ -64,7 +61,7 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => { ); return ( <LocationCardWrapper - bg={bg} + bg="white" key={label} whileHover={{ scale: 1.05 }} borderColor={borderColor} @@ -76,7 +73,6 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => { <> <Flex justifyContent="space-between" alignItems="center"> <chakra.h2 - color={fg} fontWeight="bold" mt={{ base: 2, md: 0 }} fontSize={{ base: 'lg', md: 'xl' }} @@ -84,7 +80,7 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => { {label} </chakra.h2> <Avatar - color={fg} + color="black" name={label} boxSize={12} rounded="full" @@ -97,7 +93,7 @@ export const LocationCard = (props: LocationCardProps): JSX.Element => { </Flex> {option?.data?.description && ( - <chakra.p mt={2} color={fg} opacity={0.6} fontSize="sm"> + <chakra.p mt={2} opacity={0.6} fontSize="sm"> {option.data.description as string} </chakra.p> )} diff --git a/hyperglass/ui/components/looking-glass-form.tsx b/hyperglass/ui/components/looking-glass-form.tsx index 664f8a8..65ec9ae 100644 --- a/hyperglass/ui/components/looking-glass-form.tsx +++ b/hyperglass/ui/components/looking-glass-form.tsx @@ -173,7 +173,7 @@ export const LookingGlassForm = (): JSX.Element => { w="100%" mx="auto" textAlign="left" - maxW={{ base: '100%', lg: '75%' }} + maxW="100%" onSubmit={handleSubmit(submitHandler)} > <FormRow> diff --git a/hyperglass/ui/components/meta.tsx b/hyperglass/ui/components/meta.tsx index a5c20ae..22275b6 100644 --- a/hyperglass/ui/components/meta.tsx +++ b/hyperglass/ui/components/meta.tsx @@ -7,7 +7,7 @@ export const Meta = (): JSX.Element => { const [location, setLocation] = useState('/'); const { - siteTitle: title = 'hyperglass', + siteTitle: title = 'Looking Glass', siteDescription: description = 'Network Looking Glass', } = useConfig(); @@ -23,16 +23,13 @@ export const Meta = (): JSX.Element => { <Head> <title key="title">{title} + - - + ); }; diff --git a/hyperglass/ui/components/output/fields.tsx b/hyperglass/ui/components/output/fields.tsx index a24857a..b8efb8a 100644 --- a/hyperglass/ui/components/output/fields.tsx +++ b/hyperglass/ui/components/output/fields.tsx @@ -147,8 +147,6 @@ export const ASPath = (props: ASPathProps): JSX.Element => { export const Communities = (props: CommunitiesProps): JSX.Element => { const { communities } = props; const { web } = useConfig(); - const bg = useColorValue('white', 'gray.900'); - const color = useOpposingColor(bg); return ( @@ -165,10 +163,10 @@ export const Communities = (props: CommunitiesProps): JSX.Element => { { const { onOpen } = props; return ( - diff --git a/hyperglass/ui/components/prompt/desktop.tsx b/hyperglass/ui/components/prompt/desktop.tsx index e9cdd1c..cba88ae 100644 --- a/hyperglass/ui/components/prompt/desktop.tsx +++ b/hyperglass/ui/components/prompt/desktop.tsx @@ -6,19 +6,17 @@ import { PopoverContent, PopoverCloseButton, } from '@chakra-ui/react'; -import { useColorValue } from '~/hooks'; import type { PromptProps } from './types'; export const DesktopPrompt = (props: PromptProps): JSX.Element => { const { trigger, children, ...disclosure } = props; - const bg = useColorValue('white', 'gray.900'); return ( {trigger} - - + + {children} diff --git a/hyperglass/ui/components/prompt/mobile.tsx b/hyperglass/ui/components/prompt/mobile.tsx index 47c0da5..bbc4c02 100644 --- a/hyperglass/ui/components/prompt/mobile.tsx +++ b/hyperglass/ui/components/prompt/mobile.tsx @@ -1,11 +1,9 @@ import { Modal, ModalBody, ModalOverlay, ModalContent, ModalCloseButton } from '@chakra-ui/react'; -import { useColorValue } from '~/hooks'; import type { PromptProps } from './types'; export const MobilePrompt = (props: PromptProps): JSX.Element => { const { children, trigger, ...disclosure } = props; - const bg = useColorValue('white', 'gray.900'); return ( <> {trigger} @@ -18,7 +16,7 @@ export const MobilePrompt = (props: PromptProps): JSX.Element => { {...disclosure} > - + {children} diff --git a/hyperglass/ui/components/query-location.tsx b/hyperglass/ui/components/query-location.tsx index c8f040f..e379d09 100644 --- a/hyperglass/ui/components/query-location.tsx +++ b/hyperglass/ui/components/query-location.tsx @@ -120,10 +120,10 @@ export const QueryLocation = (props: QueryLocationProps): JSX.Element => { <> {options.length === 1 ? ( {options[0].options.map(opt => { diff --git a/hyperglass/ui/components/query-target.tsx b/hyperglass/ui/components/query-target.tsx index 1d452f9..c5827d7 100644 --- a/hyperglass/ui/components/query-target.tsx +++ b/hyperglass/ui/components/query-target.tsx @@ -7,7 +7,7 @@ import { useDirective, useFormState } from '~/hooks'; import { isSelectDirective } from '~/types'; import { UserIP } from './user-ip'; -import { type UseFormRegister, useForm } from 'react-hook-form'; +import { type UseFormRegister } from 'react-hook-form'; import type { GroupBase, OptionProps } from 'react-select'; import type { SelectOnChange } from '~/components/select'; import type { Directive, FormData, OnChangeArgs, SingleOption } from '~/types'; @@ -87,7 +87,7 @@ export const QueryTarget = (props: QueryTargetProps): JSX.Element => { { name="queryTargetDisplay" onChange={handleInputChange} _placeholder={{ color: 'gray.600' }} - _dark={{ - bg: 'blackSolid.800', - color: 'whiteAlpha.800', - borderColor: 'whiteAlpha.50', - _placeholder: { color: 'whiteAlpha.700' }, - }} /> diff --git a/hyperglass/ui/components/reset-button.tsx b/hyperglass/ui/components/reset-button.tsx index c0ea69f..f11090c 100644 --- a/hyperglass/ui/components/reset-button.tsx +++ b/hyperglass/ui/components/reset-button.tsx @@ -13,18 +13,16 @@ interface ResetButtonProps extends FlexProps { export const ResetButton = (props: ResetButtonProps): JSX.Element => { const { developerMode, resetForm, ...rest } = props; const status = useFormState(s => s.status); - const bg = useColorValue('primary.500', 'primary.300'); - const color = useOpposingColor(bg); return ( {status === 'results' && ( { const displayTarget = useFormState(s => s.target.display); const setFormValue = useFormState(s => s.setFormValue); - const color = useColorValue('secondary.500', 'secondary.300'); - const errorColor = useColorValue('red.500', 'red.300'); + const color = 'secondary.500'; + const errorColor = 'red.500'; const tooltip4 = strF(web.text.fqdnTooltip, { protocol: 'IPv4' }); const tooltip6 = strF(web.text.fqdnTooltip, { protocol: 'IPv6' }); diff --git a/hyperglass/ui/components/results/copy-button.tsx b/hyperglass/ui/components/results/copy-button.tsx index fddd87e..01ca6d2 100644 --- a/hyperglass/ui/components/results/copy-button.tsx +++ b/hyperglass/ui/components/results/copy-button.tsx @@ -18,7 +18,7 @@ export const CopyButton = (props: CopyButtonProps): JSX.Element => { size="sm" variant="ghost" onClick={onCopy} - colorScheme="secondary" + colorScheme="primary" {...rest} > diff --git a/hyperglass/ui/components/results/header.tsx b/hyperglass/ui/components/results/header.tsx index f82a62a..2089493 100644 --- a/hyperglass/ui/components/results/header.tsx +++ b/hyperglass/ui/components/results/header.tsx @@ -2,7 +2,7 @@ import { AccordionIcon, Box, HStack, Spinner, Text, Tooltip } from '@chakra-ui/r import { useMemo } from 'react'; import { useConfig } from '~/context'; import { DynamicIcon } from '~/elements'; -import { useColorValue, useOpposingColor, useStrf } from '~/hooks'; +import { useOpposingColor, useStrf } from '~/hooks'; import type { ErrorLevels } from '~/types'; @@ -26,9 +26,9 @@ const runtimeText = (runtime: number, text: string): string => { export const ResultHeader = (props: ResultHeaderProps): JSX.Element => { const { title, loading, isError, errorMsg, errorLevel, runtime } = props; - const status = useColorValue('primary.500', 'primary.300'); - const warning = useColorValue(`${errorLevel}.500`, `${errorLevel}.300`); - const defaultStatus = useColorValue('success.500', 'success.300'); + const status = 'primary.500'; + const warning = `${errorLevel}.500`; + const defaultStatus = 'success.500'; const { web } = useConfig(); const strF = useStrf(); diff --git a/hyperglass/ui/components/results/individual.tsx b/hyperglass/ui/components/results/individual.tsx index a316dd3..e1eeb49 100644 --- a/hyperglass/ui/components/results/individual.tsx +++ b/hyperglass/ui/components/results/individual.tsx @@ -48,6 +48,7 @@ const AccordionHeaderWrapper = chakra('div', { baseStyle: { display: 'flex', justifyContent: 'space-between', + bg: 'white', _hover: { bg: 'blackAlpha.50' }, _focus: { boxShadow: 'outline' }, }, @@ -223,6 +224,7 @@ const _Result: React.ForwardRefRenderFunction = ( } - colorScheme="secondary" + colorScheme="primary" {...rest} > diff --git a/hyperglass/ui/components/select/option.tsx b/hyperglass/ui/components/select/option.tsx index fe9267c..d6265bf 100644 --- a/hyperglass/ui/components/select/option.tsx +++ b/hyperglass/ui/components/select/option.tsx @@ -19,7 +19,7 @@ export const Option = ( display={{ base: 'flex', lg: 'inline-flex' }} > {tags.map(tag => ( - + {tag} ))} diff --git a/hyperglass/ui/components/select/styles.ts b/hyperglass/ui/components/select/styles.ts index e1e20b2..c1eccfc 100644 --- a/hyperglass/ui/components/select/styles.ts +++ b/hyperglass/ui/components/select/styles.ts @@ -3,8 +3,6 @@ import { mergeWith } from '@chakra-ui/utils'; /* eslint-disable react-hooks/exhaustive-deps */ import { useCallback } from 'react'; import { - useColorToken, - useColorValue, useMobile, useOpposingColor, useOpposingColorCallback, @@ -30,26 +28,20 @@ export const useControlStyle = { const { isFocused } = state; const styles = { - backgroundColor, - borderRadius, - color, - minHeight, + backgroundColor: useToken('colors', 'white'), + borderRadius: useToken('radii', 'md'), + color: useToken('colors', 'black'), + minHeight: useToken('space', 12), transition: 'all 0.2s', - borderColor: isError ? invalidBorder : isFocused ? focusBorder : borderColor, + borderColor: isError ? invalidBorder : isFocused ? focusBorder : useToken('gray.100'), boxShadow: isError ? `0 0 0 1px ${invalidBorder}` : isFocused @@ -72,9 +64,7 @@ export const useMenuStyle = ( const { colorMode } = props; const { isOpen } = useSelectContext(); - - const backgroundColor = useColorToken('colors', 'white', 'blackSolid.700'); - const styles = { backgroundColor, zIndex: 1500 }; + const styles = { backgroundColor: useToken('white'), zIndex: 1500 }; return useCallback(base => mergeWith({}, base, styles), [colorMode, isOpen]); }; @@ -86,14 +76,12 @@ export const useMenuListStyle = { @@ -136,12 +123,11 @@ export const useOptionStyle = => { const { colorMode } = props; - const backgroundColor = useColorToken('colors', 'gray.200', 'whiteAlpha.300'); - const styles = { backgroundColor }; + const styles = { backgroundColor: useToken('colors', 'gray.200') }; return useCallback(base => mergeWith({}, base, styles), [colorMode]); }; @@ -167,10 +152,12 @@ export const usePlaceholderStyle = => { const { colorMode } = props; - const color = useColorToken('colors', 'gray.600', 'whiteAlpha.700'); - const fontSize = useToken('fontSizes', 'lg'); + const styles = { + color: useToken('colors', 'gray.600'), + fontSize: useToken('fontSizes', 'lg') + } - return useCallback(base => mergeWith({}, base, { color, fontSize }), [colorMode]); + return useCallback(base => mergeWith({}, base, styles), [colorMode]); }; export const useSingleValueStyle = ( @@ -178,9 +165,11 @@ export const useSingleValueStyle = => { const { colorMode } = props; - const color = useColorValue('black', 'whiteAlpha.800'); - const fontSize = useToken('fontSizes', 'lg'); - const styles = { color, fontSize }; + const color = useToken('colors', 'black'); + const styles = { + color, + fontSize: useToken('fontSizes', 'lg') + }; return useCallback(base => mergeWith({}, base, styles), [color, colorMode]); }; @@ -190,10 +179,12 @@ export const useMultiValueStyle = => { const { colorMode } = props; - const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300'); - const color = useOpposingColor(backgroundColor); - const borderRadius = useToken('radii', 'md'); - const styles = { backgroundColor, color, borderRadius }; + const backgroundColor = useToken('colors', 'brand.500'); + const styles = { + backgroundColor: backgroundColor, + color: useToken('colors', 'white'), + borderRadius: useToken('radii', 'md') + }; return useCallback(base => mergeWith({}, base, styles), [backgroundColor, colorMode]); }; @@ -203,9 +194,7 @@ export const useMultiValueLabelStyle = => { const { colorMode } = props; - const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300'); - const color = useOpposingColor(backgroundColor); - const styles = { color }; + const styles = { color: useToken('colors', 'white') }; return useCallback(base => mergeWith({}, base, styles), [colorMode]); }; @@ -215,10 +204,9 @@ export const useMultiValueRemoveStyle = => { const { colorMode } = props; - const backgroundColor = useColorToken('colors', 'primary.500', 'primary.300'); - const color = useOpposingColor(backgroundColor); + const color = useToken('colors', 'white'); const styles = { - color, + color: color, '&:hover': { backgroundColor: 'transparent', color, opacity: 0.8 }, }; diff --git a/hyperglass/ui/components/submit-button.tsx b/hyperglass/ui/components/submit-button.tsx index ef4cbd5..65c4011 100644 --- a/hyperglass/ui/components/submit-button.tsx +++ b/hyperglass/ui/components/submit-button.tsx @@ -17,7 +17,7 @@ import { useFormContext } from 'react-hook-form'; import { Else, If, Then } from 'react-if'; import { ResolvedTarget } from '~/components'; import { DynamicIcon } from '~/elements'; -import { useColorValue, useFormState, useMobile } from '~/hooks'; +import { useFormState, useMobile } from '~/hooks'; import type { IconButtonProps } from '@chakra-ui/react'; @@ -42,7 +42,8 @@ const _SubmitIcon: React.ForwardRefRenderFunction< type="submit" icon={} title="Submit Query" - colorScheme="primary" + variant="solid" + colorScheme="brand" isLoading={isLoading} aria-label="Submit Query" {...rest} @@ -56,7 +57,6 @@ const SubmitIcon = forwardRef { const { children, isOpen, onClose } = props; - const bg = useColorValue('white', 'gray.900'); return ( <> {children} @@ -70,7 +70,7 @@ const MSubmitButton = (props: ResponsiveSubmitButtonProps): JSX.Element => { motionPreset="slideInBottom" > - + {isOpen && } @@ -86,12 +86,11 @@ const MSubmitButton = (props: ResponsiveSubmitButtonProps): JSX.Element => { */ const DSubmitButton = (props: ResponsiveSubmitButtonProps): JSX.Element => { const { children, isOpen, onClose } = props; - const bg = useColorValue('white', 'gray.900'); return ( {children} - - + + {isOpen && } diff --git a/hyperglass/ui/components/user-ip.tsx b/hyperglass/ui/components/user-ip.tsx index 7a31630..66b0b4b 100644 --- a/hyperglass/ui/components/user-ip.tsx +++ b/hyperglass/ui/components/user-ip.tsx @@ -38,7 +38,7 @@ export const UserIP = (props: UserIPProps): JSX.Element => { return ( + } diff --git a/hyperglass/ui/elements/card/body.tsx b/hyperglass/ui/elements/card/body.tsx index 9d879bc..e06ce90 100644 --- a/hyperglass/ui/elements/card/body.tsx +++ b/hyperglass/ui/elements/card/body.tsx @@ -1,5 +1,4 @@ import { Flex } from '@chakra-ui/react'; -import { useColorValue } from '~/hooks'; import type { FlexProps } from '@chakra-ui/react'; @@ -9,14 +8,12 @@ interface CardBodyProps extends Omit { export const CardBody = (props: CardBodyProps): JSX.Element => { const { onClick, ...rest } = props; - const bg = useColorValue('white', 'dark.500'); - const color = useColorValue('dark.500', 'white'); return ( { - const bg = useColorValue('blackAlpha.100', 'gray.800'); - const color = useColorValue('black', 'white'); return ( { const time = [zeroPad(seconds)]; minutes !== 0 && time.unshift(zeroPad(minutes)); hours !== 0 && time.unshift(zeroPad(hours)); - const bg = useColorValue('black', 'white'); return ( @@ -28,7 +27,7 @@ const Renderer = (props: RendererProps): JSX.Element => { {text} - + {time.join(':')} diff --git a/hyperglass/ui/pages/_document.tsx b/hyperglass/ui/pages/_document.tsx index 09869d7..3a2c443 100644 --- a/hyperglass/ui/pages/_document.tsx +++ b/hyperglass/ui/pages/_document.tsx @@ -1,5 +1,4 @@ import fs from 'fs'; -import { ColorModeScript } from '@chakra-ui/react'; import Document, { Html, Head, Main, NextScript } from 'next/document'; import { CustomHtml, CustomJavascript, Favicon } from '~/elements'; import { googleFontUrl } from '~/util'; @@ -42,7 +41,7 @@ class MyDocument extends Document { // } = await getHyperglassConfig(hyperglassUrl); fonts = { - body: googleFontUrl(config.web.theme.fonts.body), + body: "https://assets.witine.com/fonts/inter.css", mono: googleFontUrl(config.web.theme.fonts.mono), }; defaultColorMode = config.web.theme.defaultColorMode; @@ -61,9 +60,6 @@ class MyDocument extends Document { - - - {favicons.map(favicon => ( @@ -72,7 +68,6 @@ class MyDocument extends Document { {this.props.customJs} -
{this.props.customHtml} diff --git a/hyperglass/ui/types/config.ts b/hyperglass/ui/types/config.ts index 82b8912..84d6b18 100644 --- a/hyperglass/ui/types/config.ts +++ b/hyperglass/ui/types/config.ts @@ -59,6 +59,7 @@ interface _Logo { height: string | null; light_format: string; dark_format: string; + footer_format: string; } interface _Link { @@ -87,6 +88,7 @@ interface _Highlight { } interface _Web { + copyright: string; credit: _Credit; dns_provider: { name: string; url: string }; links: _Link[]; diff --git a/hyperglass/ui/util/theme.ts b/hyperglass/ui/util/theme.ts index 6e891c4..96fe53c 100644 --- a/hyperglass/ui/util/theme.ts +++ b/hyperglass/ui/util/theme.ts @@ -16,6 +16,7 @@ function importFonts(userFonts: Theme.Fonts): ChakraTheme['fonts'] { function importColors(userColors: ThemeConfig['colors']): Theme.Colors { const initial: Pick = { + brand: generatePalette('#00BCA9'), blackSolid: { 50: '#444444', 100: '#3c3c3c', @@ -92,24 +93,14 @@ export function makeTheme( colors, config, fontWeights, - semanticTokens: { - colors: { - 'body-bg': { - default: 'light.500', - _dark: 'dark.500', - }, - 'body-fg': { - default: 'dark.500', - _dark: 'light.500', - }, - }, - }, styles: { global: { html: { scrollBehavior: 'smooth', height: '-webkit-fill-available' }, body: { - background: 'body-bg', - color: 'body-fg', + background: 'repeating-linear-gradient(225deg,#f5fffe,#effdff,#eff9ff,#f5f4ff,#ffeefe,#ffeefe)', + color: '#2D3635', + fontSize: '1.05rem', + lineHeight: '1.5', overflowX: 'hidden', }, },