Skip to content

Commit

Permalink
css: ux improvements for bland (#788)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored May 3, 2024
1 parent f3fa828 commit 869796d
Show file tree
Hide file tree
Showing 16 changed files with 131 additions and 60 deletions.
9 changes: 1 addition & 8 deletions packages/ui/app/src/api-page/endpoints/EndpointParameter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import cn from "clsx";
import type { MDXRemoteSerializeResult } from "next-mdx-remote";
import { FC, PropsWithChildren, ReactNode, memo, useRef, useState } from "react";
import { AbsolutelyPositionedAnchor } from "../../commons/AbsolutelyPositionedAnchor";
import { MonospaceText } from "../../commons/monospace/MonospaceText";
import { useRouteListener } from "../../contexts/useRouteListener";
import { ResolvedTypeDefinition, ResolvedTypeShape } from "../../resolver/types";
import { getAnchorId } from "../../util/anchor";
Expand Down Expand Up @@ -102,13 +101,7 @@ export const EndpointParameterContent: FC<PropsWithChildren<EndpointParameter.Co
<div className="group/anchor-container flex items-center">
<AbsolutelyPositionedAnchor href={anchorRoute} />
<span className="inline-flex items-baseline gap-2">
<MonospaceText
className={cn("t-default text-sm", {
"t-accent": isActive,
})}
>
{name}
</MonospaceText>
<span className="fern-api-property-key">{name}</span>
{typeShorthand}
{availability != null && <EndpointAvailabilityTag availability={availability} minimal={true} />}
</span>
Expand Down
12 changes: 11 additions & 1 deletion packages/ui/app/src/api-page/examples/CodeSnippetExample.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import clsx from "clsx";
import { createRef, FC, useCallback, useEffect, useMemo } from "react";
import { FernErrorBoundary } from "../../components/FernErrorBoundary";
import { useFeatureFlags } from "../../contexts/FeatureFlagContext";
import { FernSyntaxHighlighter } from "../../syntax-highlighting/FernSyntaxHighlighter";
import { ScrollToHandle } from "../../syntax-highlighting/FernSyntaxHighlighterTokens";
import { getJsonLineNumbers } from "./getJsonLineNumbers";
Expand Down Expand Up @@ -29,8 +31,10 @@ const CodeSnippetExampleInternal: FC<CodeSnippetExample.Props> = ({
jsonStartLine,
scrollAreaStyle,
measureHeight,
className,
...props
}) => {
const { isDarkCodeEnabled } = useFeatureFlags();
const codeBlockRef = createRef<HTMLPreElement>();
const viewportRef = createRef<ScrollToHandle>();

Expand Down Expand Up @@ -70,7 +74,13 @@ const CodeSnippetExampleInternal: FC<CodeSnippetExample.Props> = ({
}, [requestHighlightLines, viewportRef]);

return (
<TitledExample copyToClipboardText={useCallback(() => code, [code])} {...props}>
<TitledExample
copyToClipboardText={useCallback(() => code, [code])}
{...props}
className={clsx(className, {
"dark bg-card": isDarkCodeEnabled,
})}
>
<FernSyntaxHighlighter
id={id}
className="rounded-t-0 rounded-b-[inherit]"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { titleCase } from "@fern-ui/core-utils";
import cn from "clsx";
import { startCase } from "lodash-es";
import { useCallback, useMemo } from "react";
import { MonospaceText } from "../../../commons/monospace/MonospaceText";
import {
ResolvedDiscriminatedUnionShapeVariant,
ResolvedTypeDefinition,
Expand Down Expand Up @@ -84,7 +83,7 @@ export const DiscriminatedUnionVariant: React.FC<DiscriminatedUnionVariant.Props
"px-3": !isRootTypeDefinition,
})}
>
<MonospaceText className="t-default text-sm">{startCase(unionVariant.discriminantValue)}</MonospaceText>
<span className="fern-api-property-key">{titleCase(unionVariant.discriminantValue)}</span>
{unionVariant.availability != null && (
<EndpointAvailabilityTag availability={unionVariant.availability} minimal={true} />
)}
Expand Down
9 changes: 3 additions & 6 deletions packages/ui/app/src/api-page/types/object/ObjectProperty.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import cn from "clsx";
import { forwardRef, memo, useCallback, useMemo, useRef, useState } from "react";
import { AbsolutelyPositionedAnchor } from "../../../commons/AbsolutelyPositionedAnchor";
import { MonospaceText } from "../../../commons/monospace/MonospaceText";
import { FernErrorBoundary } from "../../../components/FernErrorBoundary";
import { useRouteListener } from "../../../contexts/useRouteListener";
import { ResolvedObjectProperty, ResolvedTypeDefinition, unwrapDescription } from "../../../resolver/types";
Expand Down Expand Up @@ -118,15 +117,13 @@ const UnmemoizedObjectPropertyInternal = forwardRef<HTMLDivElement, ObjectProper
<div className="flex items-baseline gap-2">
<div className="group/anchor-container relative inline-flex items-center">
<AbsolutelyPositionedAnchor href={anchorRoute} smallGap />
<MonospaceText
className={cn("t-default text-sm", {
"t-accent": isActive,
})}
<span
className="fern-api-property-key"
onMouseEnter={onMouseEnterPropertyName}
onMouseOut={onMouseOutPropertyName}
>
{property.key}
</MonospaceText>
</span>
</div>
{renderTypeShorthandRoot(property.valueShape, types, contextValue.isResponse)}
{property.availability != null && (
Expand Down
2 changes: 2 additions & 0 deletions packages/ui/app/src/contexts/FeatureFlagContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface FeatureFlags {
isTocDefaultEnabled: boolean;
isSnippetTemplatesEnabled: boolean;
isHttpSnippetsEnabled: boolean;
isDarkCodeEnabled: boolean;
}

export const DEFAULT_FEATURE_FLAGS: FeatureFlags = {
Expand All @@ -18,6 +19,7 @@ export const DEFAULT_FEATURE_FLAGS: FeatureFlags = {
isTocDefaultEnabled: false,
isSnippetTemplatesEnabled: false,
isHttpSnippetsEnabled: false,
isDarkCodeEnabled: false,
};

export const FeatureFlagContext = createContext<FeatureFlags>(DEFAULT_FEATURE_FLAGS);
Expand Down
12 changes: 6 additions & 6 deletions packages/ui/app/src/mdx/components/Card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export declare namespace Card {
export const Card: React.FC<Card.Props> = ({
title,
icon,
iconSize,
iconSize = 8,
color,
darkModeColor,
lightModeColor,
Expand All @@ -35,7 +35,7 @@ export const Card: React.FC<Card.Props> = ({
href,
badge,
}) => {
const className = cn("text-sm border p-4 not-prose rounded-lg relative block");
const className = cn("text-base border p-6 not-prose rounded-xl relative block");

const content = (
<>
Expand Down Expand Up @@ -63,16 +63,16 @@ export const Card: React.FC<Card.Props> = ({
<span
className="card-icon"
style={{
width: iconSize != null ? `${iconSize * 4}px` : undefined,
height: iconSize != null ? `${iconSize * 4}px` : undefined,
width: `${iconSize * 4}px`,
height: `${iconSize * 4}px`,
}}
>
{icon}
</span>
) : null}
<div>
<div className="space-y-1">
<div className="t-default text-base font-semibold">{title}</div>
{children != null && <div className="t-muted mt-1 leading-snug">{children}</div>}
{children != null && <div className="t-muted">{children}</div>}
</div>
</div>
</>
Expand Down
10 changes: 5 additions & 5 deletions packages/ui/app/src/mdx/components/CardGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export declare namespace CardGroup {
export const CardGroup: FC<PropsWithChildren<CardGroup.Props>> = ({ children, cols = 2 }) => {
return (
<div
className={cn("grid gap-4 sm:gap-6 mb-6 mt-4 first:mt-0", {
className={cn("grid gap-4 sm:gap-6 my-6 first:mt-0", {
"grid-cols-1": cols <= 1,
"grid-cols-1 sm:grid-cols-2": cols === 2,
"grid-cols-1 sm:grid-cols-2 md:grid-cols-3": cols === 3,
"grid-cols-1 sm:grid-cols-2 md:grid-cols-4": cols === 4,
"grid-cols-1 sm:grid-cols-2 md:grid-cols-5": cols === 5,
"grid-cols-1 sm:grid-cols-2 md:grid-cols-6": cols >= 6,
"grid-cols-1 sm:grid-cols-2 xl:grid-cols-3": cols === 3,
"grid-cols-1 sm:grid-cols-2 xl:grid-cols-4": cols === 4,
"grid-cols-1 sm:grid-cols-2 xl:grid-cols-5": cols === 5,
"grid-cols-1 sm:grid-cols-2 xl:grid-cols-6": cols >= 6,
})}
>
{children}
Expand Down
11 changes: 9 additions & 2 deletions packages/ui/app/src/mdx/components/CodeGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as Tabs from "@radix-ui/react-tabs";
import clsx from "clsx";
import { useState } from "react";
import { HorizontalOverflowMask } from "../../commons/HorizontalOverflowMask";
import { useFeatureFlags } from "../../contexts/FeatureFlagContext";
import { CopyToClipboardButton } from "../../syntax-highlighting/CopyToClipboardButton";
import { FernSyntaxHighlighter, FernSyntaxHighlighterProps } from "../../syntax-highlighting/FernSyntaxHighlighter";

Expand All @@ -15,10 +17,15 @@ export declare namespace CodeGroup {
}

export const CodeGroup: React.FC<React.PropsWithChildren<CodeGroup.Props>> = ({ items }) => {
const { isDarkCodeEnabled } = useFeatureFlags();
const [selectedTabIndex, setSelectedTabIndex] = useState(0);

const containerClass =
"after:ring-default bg-card relative mt-4 first:mt-0 mb-6 flex w-full min-w-0 max-w-full flex-col rounded-lg shadow-sm after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:ring-1 after:ring-inset after:content-['']";
const containerClass = clsx(
"after:ring-default bg-card relative mt-4 first:mt-0 mb-6 flex w-full min-w-0 max-w-full flex-col rounded-lg shadow-sm after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:ring-1 after:ring-inset after:content-['']",
{
"dark bg-card": isDarkCodeEnabled,
},
);

if (items.length === 1 && items[0] != null) {
return (
Expand Down
6 changes: 5 additions & 1 deletion packages/ui/app/src/next-app/globals.scss
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@

&.interactive {
@apply cursor-pointer shadow-card;
@apply hover:shadow-card-elevated hover:border-primary transition-all;
@apply hover:shadow-card-elevated hover:border-accent-primary transition-all;
@apply active:shadow-card-elevated active:border-primary;

&.active {
Expand Down Expand Up @@ -817,4 +817,8 @@
}
}
}

.fern-api-property-key {
@apply font-mono t-default font-semibold text-sm;
}
}
85 changes: 63 additions & 22 deletions packages/ui/app/src/next-app/utils/getColorVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,30 @@ export const CSS_VARIABLES = {
GRAYSCALE_10: "--grayscale-a10",
GRAYSCALE_11: "--grayscale-a11",
GRAYSCALE_12: "--grayscale-a12",
GRAYSCALE_1_LIGHT: "--grayscale-light-1",
GRAYSCALE_2_LIGHT: "--grayscale-light-2",
GRAYSCALE_3_LIGHT: "--grayscale-light-3",
GRAYSCALE_4_LIGHT: "--grayscale-light-4",
GRAYSCALE_5_LIGHT: "--grayscale-light-5",
GRAYSCALE_6_LIGHT: "--grayscale-light-6",
GRAYSCALE_7_LIGHT: "--grayscale-light-7",
GRAYSCALE_8_LIGHT: "--grayscale-light-8",
GRAYSCALE_9_LIGHT: "--grayscale-light-9",
GRAYSCALE_10_LIGHT: "--grayscale-light-10",
GRAYSCALE_11_LIGHT: "--grayscale-light-11",
GRAYSCALE_12_LIGHT: "--grayscale-light-12",
GRAYSCALE_1_DARK: "--grayscale-dark-1",
GRAYSCALE_2_DARK: "--grayscale-dark-2",
GRAYSCALE_3_DARK: "--grayscale-dark-3",
GRAYSCALE_4_DARK: "--grayscale-dark-4",
GRAYSCALE_5_DARK: "--grayscale-dark-5",
GRAYSCALE_6_DARK: "--grayscale-dark-6",
GRAYSCALE_7_DARK: "--grayscale-dark-7",
GRAYSCALE_8_DARK: "--grayscale-dark-8",
GRAYSCALE_9_DARK: "--grayscale-dark-9",
GRAYSCALE_10_DARK: "--grayscale-dark-10",
GRAYSCALE_11_DARK: "--grayscale-dark-11",
GRAYSCALE_12_DARK: "--grayscale-dark-12",
BODY_TEXT: "--body-text",
BODY_TEXT_INVERTED: "--body-text-inverted",
BACKGROUND_IMAGE: "--docs-background-image",
Expand Down Expand Up @@ -110,9 +134,6 @@ export function getColorVariables(
const backgroundColorLight = enforceBackgroundTheme(getColor(colorsV3, "background", "light"), "light").toRgb();
const backgroundColorDark = enforceBackgroundTheme(getColor(colorsV3, "background", "dark"), "dark").toRgb();

const radixGrayscaleLight = getClosestGrayColor(tinycolor(backgroundColorLight));
const radixGrayscaleDark = getClosestGrayColor(tinycolor(backgroundColorDark));

const accentPrimaryLightUi = increaseForegroundContrast(
getColor(colorsV3, "accentPrimary", "light"),
tinycolor(backgroundColorLight),
Expand All @@ -124,6 +145,9 @@ export function getColorVariables(
"ui",
).toRgb();

const radixGrayscaleLight = getBestColorScale(tinycolor(accentPrimaryLightUi));
const radixGrayscaleDark = getBestColorScale(tinycolor(accentPrimaryDarkUi));

const accentPrimaryLightContrast = tinycolor(accentPrimaryLightUi).isLight()
? { r: 0, g: 0, b: 0 }
: { r: 255, g: 255, b: 255 };
Expand Down Expand Up @@ -224,7 +248,8 @@ export function getColorVariables(
[CSS_VARIABLES.ACCENT_PRIMARY_TINTED]: `${accentPrimaryDarkTinted.r}, ${accentPrimaryDarkTinted.g}, ${accentPrimaryDarkTinted.b}`,
[CSS_VARIABLES.BACKGROUND]: `${backgroundColorDark.r}, ${backgroundColorDark.g}, ${backgroundColorDark.b}`,
[CSS_VARIABLES.ACCENT_PRIMARY_CONTRAST]: `${accentPrimaryDarkContrast.r}, ${accentPrimaryDarkContrast.g}, ${accentPrimaryDarkContrast.b}`,
[CSS_VARIABLES.CARD_BACKGROUND]: cardBackgroundDark?.toRgbString() ?? "var(--grayscale-a2)",
[CSS_VARIABLES.CARD_BACKGROUND]:
cardBackgroundDark?.toRgbString() ?? tinycolor(backgroundColorDark).darken(2).toRgbString(),
[CSS_VARIABLES.SIDEBAR_BACKGROUND]: sidebarBackgroundDark?.toRgbString() ?? "transparent",
[CSS_VARIABLES.HEADER_BACKGROUND]: headerBackgroundDark?.toRgbString() ?? "transparent",
[CSS_VARIABLES.BORDER]: borderDark?.toRgbString() ?? "var(--grayscale-a5)",
Expand Down Expand Up @@ -292,13 +317,9 @@ function getOppositeBrightness(color: tinycolor.Instance | undefined): tinycolor
}

function getColorDistance(color1: tinycolor.Instance, color2: tinycolor.Instance): number {
const rgb1 = color1.toRgb();
const rgb2 = color2.toRgb();
const rMean = (rgb1.r + rgb2.r) / 2;
const r = rgb1.r - rgb2.r;
const g = rgb1.g - rgb2.g;
const b = rgb1.b - rgb2.b;
return Math.sqrt((2 + rMean / 256) * (r * r) + 4 * (g * g) + (2 + (255 - rMean) / 256) * (b * b));
const { r: r1, g: g1, b: b1 } = color1.toRgb();
const { r: r2, g: g2, b: b2 } = color2.toRgb();
return Math.sqrt((r1 - r2) ** 2 + (g1 - g2) ** 2 + (b1 - b2) ** 2);
}

type RadixGray = "gray" | "mauve" | "slate" | "sage" | "olive" | "sand";
Expand Down Expand Up @@ -394,19 +415,39 @@ const GRAY_COLORS = {
},
} as const;

function getClosestGrayColor(color: tinycolor.Instance): RadixGray {
let closestColor: RadixGray = "gray";
let closestDistance = Infinity;
for (const [gray, sampleColorGroup] of Object.entries(GRAY_COLORS)) {
for (const [, sampleColor] of Object.entries(sampleColorGroup)) {
const distance = getColorDistance(color, sampleColor);
if (distance < closestDistance) {
closestColor = gray as RadixGray;
closestDistance = distance;
}
// function getClosestGrayColor(color: tinycolor.Instance): RadixGray {
// let closestColor: RadixGray = "gray";
// let closestDistance = Infinity;
// for (const [gray, sampleColorGroup] of Object.entries(GRAY_COLORS)) {
// for (const [, sampleColor] of Object.entries(sampleColorGroup)) {
// const distance = getColorDistance(color, sampleColor);
// if (distance < closestDistance) {
// closestColor = gray as RadixGray;
// closestDistance = distance;
// }
// }
// }
// return closestColor;
// }

function getBestColorScale(accentColor: tinycolor.Instance): RadixGray {
let minDistance = Infinity;
let bestColorScale: RadixGray = "gray";

for (const [key, scale] of Object.entries(GRAY_COLORS)) {
const scaleColors = Object.values(scale);
const totalDistance = scaleColors.reduce((sum, color) => {
const distance = getColorDistance(accentColor, color);
return sum + distance;
}, 0);

if (totalDistance < minDistance) {
minDistance = totalDistance;
bestColorScale = key as RadixGray;
}
}
return closestColor;

return bestColorScale;
}

export function getThemeColor(config: DocsV1Read.ThemeConfig): string {
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/app/src/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const SidebarInner = memo<SidebarProps>(function SidebarInner({
>
{tabs.length > 0 && (
<ul
className={cn("mt-5 flex list-none flex-col", {
className={cn("mt-5 mb-6 flex list-none flex-col", {
"lg:hidden": layout?.disableHeader !== true && layout?.tabsPlacement === "HEADER",
})}
>
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/app/src/sidebar/SidebarApiSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ function SidebarApiSlugLink({ item, registerScrolledToPathListener, depth, api }
};
return (
<SidebarSlugLink
className={cn({
"first:mt-6": depth === 0,
})}
slug={item.slug}
shallow={shallow}
title={item.title}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import cn from "clsx";
import cn, { clsx } from "clsx";
import React, { PropsWithChildren } from "react";
import { useFeatureFlags } from "../contexts/FeatureFlagContext";
import { CopyToClipboardButton } from "./CopyToClipboardButton";

type CodeBlockWithClipboardButtonProps = {
Expand All @@ -10,8 +11,16 @@ export const CodeBlockWithClipboardButton: React.FC<PropsWithChildren<CodeBlockW
code,
children,
}) => {
const { isDarkCodeEnabled } = useFeatureFlags();
return (
<div className="not-prose group/cb-container bg-card relative mb-6 mt-4 flex w-full rounded-lg shadow-sm after:ring-default after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-1 after:ring-inset after:content-['']">
<div
className={clsx(
"not-prose group/cb-container bg-card relative mb-6 mt-4 flex w-full rounded-lg shadow-sm after:ring-default after:pointer-events-none after:absolute after:inset-0 after:rounded-lg after:ring-1 after:ring-inset after:content-['']",
{
"dark bg-card": isDarkCodeEnabled,
},
)}
>
{children}
<CopyToClipboardButton
className={cn(
Expand Down
Loading

0 comments on commit 869796d

Please sign in to comment.