From 67f7c1d5f520a7c32915e2c10f3d00acc7192925 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:53:19 -0500 Subject: [PATCH 1/9] add tooltip and contextMenu on navbar --- .vscode/settings.json | 2 + .../context-menu/context-menu.module.scss | 0 .../components/context-menu/context-menu.tsx | 33 +++++++++++++++ .../components/footer/footer.component.tsx | 6 +-- .../site-brand/site-brand.component.tsx | 21 ++++++++-- .../site-brand/site-brand.module.scss | 24 +++++++++-- .../localization/dictionaries/images/en.tsx | 40 +++++++----------- .../localization/dictionaries/images/ja.tsx | 42 ++++++++----------- .../models/brand-dictionary.model.ts | 24 +++++++++++ .../models/images-dictionary.model.ts | 17 -------- .../services/brand-dictionary.service.tsx | 13 ++++++ .../services/images-dictionary.service.tsx | 13 ------ 12 files changed, 146 insertions(+), 89 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/features/common/components/context-menu/context-menu.module.scss create mode 100644 src/features/common/components/context-menu/context-menu.tsx create mode 100644 src/features/localization/models/brand-dictionary.model.ts delete mode 100644 src/features/localization/models/images-dictionary.model.ts create mode 100644 src/features/localization/services/brand-dictionary.service.tsx delete mode 100644 src/features/localization/services/images-dictionary.service.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..7a73a41b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/src/features/common/components/context-menu/context-menu.module.scss b/src/features/common/components/context-menu/context-menu.module.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/features/common/components/context-menu/context-menu.tsx b/src/features/common/components/context-menu/context-menu.tsx new file mode 100644 index 00000000..bac0dcdb --- /dev/null +++ b/src/features/common/components/context-menu/context-menu.tsx @@ -0,0 +1,33 @@ +// src/components/ContextMenu.tsx +import React, { FC } from "react"; +import "./ContextMenu.scss"; +import { BrandMenuItem } from "@/features/localization/models/brand-dictionary.model"; + +interface ContextMenuProps { + items: BrandMenuItem[]; + position: { x: number; y: number } | null; +} + +const ContextMenu: FC = ({ items, position }) => { + if (!position) return null; + + return ( + + ); +}; + +export default ContextMenu; diff --git a/src/features/common/components/footer/footer.component.tsx b/src/features/common/components/footer/footer.component.tsx index 719ff3db..ad06e00d 100644 --- a/src/features/common/components/footer/footer.component.tsx +++ b/src/features/common/components/footer/footer.component.tsx @@ -17,7 +17,7 @@ import { createUrlPath } from "@/libs/utils/path.utils"; import { SiteBrandComponent } from "@/features/common/components/site-brand/site-brand.component"; import { Button } from "react-aria-components"; import { Auth0LogoComponent } from "../../assets/auth0-logo.component"; -import { getImageDictionary } from "@/features/localization/services/images-dictionary.service"; +import { getBrandDictionary } from "@/features/localization/services/brand-dictionary.service"; interface FooterComponentProps { languageCode: string; @@ -31,7 +31,7 @@ export const FooterComponent: React.FC = ({ const [modalState, setModalState] = useState( ModalStateValues.CLOSED, ); - const images = getImageDictionary(languageCode); + const images = getBrandDictionary(languageCode); const languagePathPrefix: string = languageCode === DEFAULT_LANGUAGE_CODE @@ -158,7 +158,7 @@ export const FooterComponent: React.FC = ({ target="_blank" href="https://auth0.com/" > - + {dictionary.copyright} diff --git a/src/features/common/components/site-brand/site-brand.component.tsx b/src/features/common/components/site-brand/site-brand.component.tsx index f7e7fd1b..1b864dd2 100644 --- a/src/features/common/components/site-brand/site-brand.component.tsx +++ b/src/features/common/components/site-brand/site-brand.component.tsx @@ -1,7 +1,7 @@ -import React, { PropsWithChildren } from "react"; +import React, { PropsWithChildren, useState } from "react"; import styles from "./site-brand.module.scss"; import Link from "next/link"; -import { getImageDictionary } from "@/features/localization/services/images-dictionary.service"; +import { getBrandDictionary } from "@/features/localization/services/brand-dictionary.service"; import { SecondaryFont } from "@/libs/theme/fonts"; import clsx from "clsx"; import { JwtLogoComponent } from "../../assets/jwt-logo.component"; @@ -16,10 +16,18 @@ export const SiteBrandComponent: React.FC = ({ path, languageCode, }) => { - const images = getImageDictionary(languageCode); + const [isVisible, setIsVisible] = useState(false); + + const brandDictionary = getBrandDictionary(languageCode); return ( - + setIsVisible(true)} + onMouseLeave={() => setIsVisible(false)} + >
@@ -29,6 +37,11 @@ export const SiteBrandComponent: React.FC = ({
Debugger
+ {isVisible && ( +
+ {brandDictionary.tooltip} +
+ )} ); }; diff --git a/src/features/common/components/site-brand/site-brand.module.scss b/src/features/common/components/site-brand/site-brand.module.scss index 09dbd56c..4b03b0ad 100644 --- a/src/features/common/components/site-brand/site-brand.module.scss +++ b/src/features/common/components/site-brand/site-brand.module.scss @@ -47,7 +47,7 @@ position: relative; display: flex; align-items: center; - height: 1rem; + height: 1rem; } .brand__headline { @@ -68,7 +68,25 @@ display: flex; font-size: 1rem; font-weight: 500; - line-height: .75rem; + line-height: 0.75rem; margin-top: 1px; - letter-spacing: .02rem; + letter-spacing: 0.02rem; +} + +.tooltip { + position: absolute; + top: -30px; + left: 50%; + transform: translateX(-50%); + padding: 4px 8px; + color: var(--color_fg_bold); + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + border-radius: 0.5rem; + font-size: 0.75rem; + white-space: nowrap; + pointer-events: none; + transition: opacity 0.2s ease-in-out; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + z-index: 1000; } diff --git a/src/features/localization/dictionaries/images/en.tsx b/src/features/localization/dictionaries/images/en.tsx index 0eb49401..cf7521f6 100644 --- a/src/features/localization/dictionaries/images/en.tsx +++ b/src/features/localization/dictionaries/images/en.tsx @@ -1,35 +1,27 @@ -import { BrandDictionaryModel } from "@/features/localization/models/images-dictionary.model"; +import { BrandDictionaryModel } from "../../models/brand-dictionary.model" export const enBrandDictionary: BrandDictionaryModel = { - title: "Right-click or long-press for logo options", + tooltip: "Right-click or long-press for logo options", menu: { brand: { label: "Brand", items: [ - {icon: "", - label: "Copy Logo SVG" - }, - {icon: "", - label: "Download Logo" - }, - {icon: "", - label: "Copy Symbol SVG" - }, - {icon: "", - label: "Download Symbol" - }, - {icon: "", - label: "Copy Wordmark SVG" - }, - {icon: "", - label: "Download Wordmark" - }, - ] + { type: "COPY", icon: "copy-icon.svg", label: "Copy Logo SVG", assetSrc: "logo.svg" }, + { type: "DOWNLOAD", icon: "download-icon.svg", label: "Download Logo", assetSrc: "logo.svg" }, + { type: "COPY", icon: "copy-icon.svg", label: "Copy Symbol SVG", assetSrc: "symbol.svg" }, + { type: "DOWNLOAD", icon: "download-icon.svg", label: "Download Symbol", assetSrc: "symbol.svg" }, + { type: "COPY", icon: "copy-icon.svg", label: "Copy Wordmark SVG", assetSrc: "wordmark.svg" }, + { type: "DOWNLOAD", icon: "download-icon.svg", label: "Download Wordmark", assetSrc: "wordmark.svg" }, + ], }, tools: { label: "Tools", items: [ - ] - } - } + { label: "Passkeys Playground", url: "https://learnpasskeys.io" }, + { label: "WebAuthn Playground", url: "https://webauthn.me" }, + { label: "OIDC Playground", url: "https://openidconnect.net" }, + { label: "SAML Tool", url: "https://samltool.io" } + ], + }, + }, }; diff --git a/src/features/localization/dictionaries/images/ja.tsx b/src/features/localization/dictionaries/images/ja.tsx index 42dc0ebb..9bb78d07 100644 --- a/src/features/localization/dictionaries/images/ja.tsx +++ b/src/features/localization/dictionaries/images/ja.tsx @@ -1,35 +1,27 @@ -import { BrandDictionaryModel } from "@/features/localization/models/images-dictionary.model"; +import { BrandDictionaryModel } from "../../models/brand-dictionary.model"; -export const jaImagesDictionary: BrandDictionaryModel = { - title: "Right-click or long-press for logo options", +export const jaBrandDictionary: BrandDictionaryModel = { + tooltip: "Right-click or long-press for logo options", menu: { brand: { label: "Brand", items: [ - {icon: "", - label: "Copy Logo SVG" - }, - {icon: "", - label: "Download Logo" - }, - {icon: "", - label: "Copy Symbol SVG" - }, - {icon: "", - label: "Download Symbol" - }, - {icon: "", - label: "Copy Wordmark SVG" - }, - {icon: "", - label: "Download Wordmark" - }, - ] + { icon: "copy-icon.svg", label: "Copy Logo SVG", assetSrc: "logo.svg" }, + { icon: "download-icon.svg", label: "Download Logo", assetSrc: "logo.svg" }, + { icon: "copy-icon.svg", label: "Copy Symbol SVG", assetSrc: "symbol.svg" }, + { icon: "download-icon.svg", label: "Download Symbol", assetSrc: "symbol.svg" }, + { icon: "copy-icon.svg", label: "Copy Wordmark SVG", assetSrc: "wordmark.svg" }, + { icon: "download-icon.svg", label: "Download Wordmark", assetSrc: "wordmark.svg" }, + ], }, tools: { label: "Tools", items: [ - ] - } - } + { label: "Passkeys Playground", url: "https://learnpasskeys.io" }, + { label: "WebAuthn Playground", url: "https://webauthn.me" }, + { label: "OIDC Playground", url: "https://openidconnect.net" }, + { label: "SAML Tool", url: "https://samltool.io" } + ], + }, + }, }; diff --git a/src/features/localization/models/brand-dictionary.model.ts b/src/features/localization/models/brand-dictionary.model.ts new file mode 100644 index 00000000..40f5ba8a --- /dev/null +++ b/src/features/localization/models/brand-dictionary.model.ts @@ -0,0 +1,24 @@ +interface ToolsMenuItem { + label: string, + url: string, +} +export interface BrandMenuItem { + type: "COPY" | "DOWNLOAD"; + icon: string; + label: string; + assetSrc: string; +} +interface MenuSection { + label: string; + items: BrandMenuItem[] | ToolsMenuItem[]; +} + +interface BrandMenu { + brand: MenuSection; + tools: MenuSection; +} + +export interface BrandDictionaryModel { + tooltip: string; + menu: BrandMenu; +} diff --git a/src/features/localization/models/images-dictionary.model.ts b/src/features/localization/models/images-dictionary.model.ts deleted file mode 100644 index 529fcde5..00000000 --- a/src/features/localization/models/images-dictionary.model.ts +++ /dev/null @@ -1,17 +0,0 @@ -interface BrandMenuItem { - icon: string; - label: string; -} -interface BrandMenuSection { - label: string; - items: BrandMenuItem[]; -} -interface BrandMenu { - brand: BrandMenuSection; - tools: BrandMenuSection; -} - -export interface BrandDictionaryModel { - title: string; - menu: BrandMenu; -} diff --git a/src/features/localization/services/brand-dictionary.service.tsx b/src/features/localization/services/brand-dictionary.service.tsx new file mode 100644 index 00000000..9afc22cb --- /dev/null +++ b/src/features/localization/services/brand-dictionary.service.tsx @@ -0,0 +1,13 @@ +import { enBrandDictionary } from "@/features/localization/dictionaries/images/en"; +import { jaBrandDictionary } from "@/features/localization/dictionaries/images/ja"; +import { BrandDictionaryModel } from "../models/brand-dictionary.model"; + +const brandDictionaries: { + [index: string]: BrandDictionaryModel; +} = { + en: enBrandDictionary, + ja: jaBrandDictionary, +}; + +export const getBrandDictionary = (language: string) => + brandDictionaries[language]; diff --git a/src/features/localization/services/images-dictionary.service.tsx b/src/features/localization/services/images-dictionary.service.tsx deleted file mode 100644 index 1a14acee..00000000 --- a/src/features/localization/services/images-dictionary.service.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { BrandDictionaryModel } from "@/features/localization/models/images-dictionary.model"; -import { enBrandDictionary } from "@/features/localization/dictionaries/images/en"; -import { jaImagesDictionary } from "@/features/localization/dictionaries/images/ja"; - -const imagesDictionaries: { - [index: string]: BrandDictionaryModel; -} = { - en: enBrandDictionary, - ja: jaImagesDictionary, -}; - -export const getImageDictionary = (language: string) => - imagesDictionaries[language]; From fe784b786cc042ca7d44b5659b0076d08ce1cc07 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Fri, 26 Sep 2025 15:15:14 -0500 Subject: [PATCH 2/9] add copy buttons to dropdown --- next.config.mjs | 26 ++++--- public/img/jwt-logo.svg | 16 ++++ public/img/jwt-symbol.svg | 13 ++++ public/img/jwt-wordmark.svg | 6 ++ .../context-menu/context-menu.module.scss | 54 +++++++++++++ .../components/context-menu/context-menu.tsx | 78 ++++++++++++++----- .../site-brand/site-brand.component.tsx | 70 +++++++++++------ .../localization/dictionaries/images/en.tsx | 24 +++--- .../localization/dictionaries/images/ja.tsx | 22 +++--- .../models/brand-dictionary.model.ts | 19 +++-- tsconfig.json | 3 +- types/svg.d.ts | 4 + 12 files changed, 255 insertions(+), 80 deletions(-) create mode 100644 public/img/jwt-logo.svg create mode 100644 public/img/jwt-symbol.svg create mode 100644 public/img/jwt-wordmark.svg create mode 100644 types/svg.d.ts diff --git a/next.config.mjs b/next.config.mjs index f4672921..8421cd46 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,18 +3,26 @@ import createMDX from "@next/mdx"; /** @type {import('next').NextConfig} */ const nextConfig = { webpack(config) { - config.module.rules.push({ - test: /\.svg$/, - use: { - loader: "@svgr/webpack", - options: { - svgoConfig: { - plugins: ["prefixIds"], + config.module.rules.push( + { + test: /\.svg$/, + resourceQuery: { not: /raw/ }, + use: { + loader: "@svgr/webpack", + options: { + svgoConfig: { + plugins: ["prefixIds"], + }, + ref: true, }, - ref: true, }, }, - }); + { + test: /\.svg$/i, + resourceQuery: /raw/, // Only apply this rule if '?raw' is present + type: "asset/source", + } + ); return config; }, diff --git a/public/img/jwt-logo.svg b/public/img/jwt-logo.svg new file mode 100644 index 00000000..54d020e9 --- /dev/null +++ b/public/img/jwt-logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/jwt-symbol.svg b/public/img/jwt-symbol.svg new file mode 100644 index 00000000..63da72ae --- /dev/null +++ b/public/img/jwt-symbol.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/jwt-wordmark.svg b/public/img/jwt-wordmark.svg new file mode 100644 index 00000000..3a2ecbd0 --- /dev/null +++ b/public/img/jwt-wordmark.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/features/common/components/context-menu/context-menu.module.scss b/src/features/common/components/context-menu/context-menu.module.scss index e69de29b..2509ca3c 100644 --- a/src/features/common/components/context-menu/context-menu.module.scss +++ b/src/features/common/components/context-menu/context-menu.module.scss @@ -0,0 +1,54 @@ +.container { + max-width: calc(-24px + 100vw); + max-height: calc(-24px + 100vh); + position: fixed; + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + font-size: 0.875rem; + z-index: 9009; + cursor: default; + border-radius: 1rem; + overflow: hidden; + padding: 0.25rem; + min-width: 180px; + box-shadow: + 0 1px 1px -0.5px rgba(0, 0, 0, 0.04), + 0 3px 3px -1.5px rgba(0, 0, 0, 0.04), + 0 6px 6px -3px rgba(0, 0, 0, 0.04), + 0 12px 12px -6px rgba(0, 0, 0, 0.04), + inset 0 0 0 1px rgba(0, 0, 0, 0.04); +} + +.groupLabel { + width: 100%; + padding: 0.5rem 0.75rem 0.25rem; + font-size: 0.8125rem; + color: var(--color_fg_default); +} + +.list { + display: flex; + align-items: center; + list-style-type: none; + flex-direction: column; +} + +.menuItem { + width: 100%; + justify-content: flex-start; + position: relative; + padding: 0.5rem 0.75rem; + border-radius: 0.75rem; + color: var(--color_fg_bold); + gap: 0.5rem; + font-family: var(--font-primary); + border: none; + background-color: var(--color_bg_layer); + display: flex; + align-items: center; + + &:hover { + cursor: pointer; + background-color: var(--color_bg_layer_alternate-bold); + } +} diff --git a/src/features/common/components/context-menu/context-menu.tsx b/src/features/common/components/context-menu/context-menu.tsx index bac0dcdb..0be5e8dc 100644 --- a/src/features/common/components/context-menu/context-menu.tsx +++ b/src/features/common/components/context-menu/context-menu.tsx @@ -1,32 +1,68 @@ -// src/components/ContextMenu.tsx -import React, { FC } from "react"; -import "./ContextMenu.scss"; -import { BrandMenuItem } from "@/features/localization/models/brand-dictionary.model"; +"use client"; + +import React, { FC, useState } from "react"; +import { BrandDictionaryModel } from "@/features/localization/models/brand-dictionary.model"; +import styles from "./context-menu.module.scss"; +import jwtLogoString from "@/public/img/jwt-logo.svg?raw"; +import jwtSymbolString from "@/public/img/jwt-symbol.svg?raw"; +import jwtWordmark from "@/public/img/jwt-wordmark.svg?raw"; interface ContextMenuProps { - items: BrandMenuItem[]; + dictionary: BrandDictionaryModel; position: { x: number; y: number } | null; } -const ContextMenu: FC = ({ items, position }) => { +const ContextMenu: FC = ({ dictionary, position }) => { + const [isCopied, setIsCopied] = useState(false); if (!position) return null; + const handleIconCopy = async (svgString: string) => { + if (!navigator.clipboard) { + console.error("Clipboard API not available"); + return; + } + try { + await navigator.clipboard.writeText(svgString); + setIsCopied(true); + + setTimeout(() => { + setIsCopied(false); + }, 2000); + } catch (err) { + console.error("Failed to copy SVG: ", err); + } + }; + return ( -
    - {items.map((item, index) => { - if (item.type === "COPY") { - return ( -
  • - {item.label} -
  • - ); - } - })} -
+
+
{dictionary.menu.brand.label}
+
    +
  • handleIconCopy(jwtLogoString)} + > +
    icon
    + {dictionary.menu.brand.svg.copyLabel} +
  • +
  • handleIconCopy(jwtSymbolString)} + > +
    icon
    + {dictionary.menu.brand.symbol.copyLabel} +
  • +
  • handleIconCopy(jwtWordmark)} + > +
    icon
    + {dictionary.menu.brand.wordmark.copyLabel} +
  • +
+
); }; diff --git a/src/features/common/components/site-brand/site-brand.component.tsx b/src/features/common/components/site-brand/site-brand.component.tsx index 1b864dd2..184a8cda 100644 --- a/src/features/common/components/site-brand/site-brand.component.tsx +++ b/src/features/common/components/site-brand/site-brand.component.tsx @@ -1,4 +1,4 @@ -import React, { PropsWithChildren, useState } from "react"; +import React, { PropsWithChildren, useEffect, useState } from "react"; import styles from "./site-brand.module.scss"; import Link from "next/link"; import { getBrandDictionary } from "@/features/localization/services/brand-dictionary.service"; @@ -6,6 +6,7 @@ import { SecondaryFont } from "@/libs/theme/fonts"; import clsx from "clsx"; import { JwtLogoComponent } from "../../assets/jwt-logo.component"; import { JwtWordmarkComponent } from "../../assets/jwt-wordmark.component"; +import ContextMenu from "../context-menu/context-menu"; interface SiteBrandComponentProps extends PropsWithChildren { path: string; @@ -16,32 +17,55 @@ export const SiteBrandComponent: React.FC = ({ path, languageCode, }) => { - const [isVisible, setIsVisible] = useState(false); + const [isTooltipVisible, setIsTooltipVisible] = useState(false); + const [menuPosition, setMenuPosition] = useState<{ + x: number; + y: number; + } | null>(null); + + const handleRightClick: React.MouseEventHandler = (e) => { + e.preventDefault(); // Prevent the browser's default context menu + setMenuPosition({ x: e.clientX, y: e.clientY }); + }; + + const handleCloseMenu = () => { + setMenuPosition(null); + }; + + useEffect(() => { + // Hide the menu on any click on the document + document.addEventListener("click", handleCloseMenu); + return () => { + document.removeEventListener("click", handleCloseMenu); + }; + }, []); const brandDictionary = getBrandDictionary(languageCode); return ( - setIsVisible(true)} - onMouseLeave={() => setIsVisible(false)} - > -
- -
-
- -
-
- Debugger -
- {isVisible && ( -
- {brandDictionary.tooltip} +
+ setIsTooltipVisible(true)} + onMouseLeave={() => setIsTooltipVisible(false)} + onContextMenu={handleRightClick} + > +
+ +
+
+ +
+
+ Debugger
- )} - + {isTooltipVisible && ( +
{brandDictionary.tooltip}
+ )} + + +
); }; diff --git a/src/features/localization/dictionaries/images/en.tsx b/src/features/localization/dictionaries/images/en.tsx index cf7521f6..1fbbbb7f 100644 --- a/src/features/localization/dictionaries/images/en.tsx +++ b/src/features/localization/dictionaries/images/en.tsx @@ -1,18 +1,22 @@ -import { BrandDictionaryModel } from "../../models/brand-dictionary.model" +import { BrandDictionaryModel } from "../../models/brand-dictionary.model"; export const enBrandDictionary: BrandDictionaryModel = { tooltip: "Right-click or long-press for logo options", menu: { brand: { label: "Brand", - items: [ - { type: "COPY", icon: "copy-icon.svg", label: "Copy Logo SVG", assetSrc: "logo.svg" }, - { type: "DOWNLOAD", icon: "download-icon.svg", label: "Download Logo", assetSrc: "logo.svg" }, - { type: "COPY", icon: "copy-icon.svg", label: "Copy Symbol SVG", assetSrc: "symbol.svg" }, - { type: "DOWNLOAD", icon: "download-icon.svg", label: "Download Symbol", assetSrc: "symbol.svg" }, - { type: "COPY", icon: "copy-icon.svg", label: "Copy Wordmark SVG", assetSrc: "wordmark.svg" }, - { type: "DOWNLOAD", icon: "download-icon.svg", label: "Download Wordmark", assetSrc: "wordmark.svg" }, - ], + svg: { + copyLabel: "Copy Logo SVG", + downloadLabel: "Download Logo", + }, + symbol: { + copyLabel: "Copy Symbol SVG", + downloadLabel: "Download Symbol", + }, + wordmark: { + copyLabel: "Copy Wordmark SVG", + downloadLabel: "Download Wordmark", + }, }, tools: { label: "Tools", @@ -20,7 +24,7 @@ export const enBrandDictionary: BrandDictionaryModel = { { label: "Passkeys Playground", url: "https://learnpasskeys.io" }, { label: "WebAuthn Playground", url: "https://webauthn.me" }, { label: "OIDC Playground", url: "https://openidconnect.net" }, - { label: "SAML Tool", url: "https://samltool.io" } + { label: "SAML Tool", url: "https://samltool.io" }, ], }, }, diff --git a/src/features/localization/dictionaries/images/ja.tsx b/src/features/localization/dictionaries/images/ja.tsx index 9bb78d07..ffc6a7e4 100644 --- a/src/features/localization/dictionaries/images/ja.tsx +++ b/src/features/localization/dictionaries/images/ja.tsx @@ -5,14 +5,18 @@ export const jaBrandDictionary: BrandDictionaryModel = { menu: { brand: { label: "Brand", - items: [ - { icon: "copy-icon.svg", label: "Copy Logo SVG", assetSrc: "logo.svg" }, - { icon: "download-icon.svg", label: "Download Logo", assetSrc: "logo.svg" }, - { icon: "copy-icon.svg", label: "Copy Symbol SVG", assetSrc: "symbol.svg" }, - { icon: "download-icon.svg", label: "Download Symbol", assetSrc: "symbol.svg" }, - { icon: "copy-icon.svg", label: "Copy Wordmark SVG", assetSrc: "wordmark.svg" }, - { icon: "download-icon.svg", label: "Download Wordmark", assetSrc: "wordmark.svg" }, - ], + svg: { + copyLabel: "Copy Logo SVG", + downloadLabel: "Download Logo", + }, + symbol: { + copyLabel: "Copy Symbol SVG", + downloadLabel: "Download Symbol", + }, + wordmark: { + copyLabel: "Copy Wordmark SVG", + downloadLabel: "Download Wordmark", + }, }, tools: { label: "Tools", @@ -20,7 +24,7 @@ export const jaBrandDictionary: BrandDictionaryModel = { { label: "Passkeys Playground", url: "https://learnpasskeys.io" }, { label: "WebAuthn Playground", url: "https://webauthn.me" }, { label: "OIDC Playground", url: "https://openidconnect.net" }, - { label: "SAML Tool", url: "https://samltool.io" } + { label: "SAML Tool", url: "https://samltool.io" }, ], }, }, diff --git a/src/features/localization/models/brand-dictionary.model.ts b/src/features/localization/models/brand-dictionary.model.ts index 40f5ba8a..912d4b36 100644 --- a/src/features/localization/models/brand-dictionary.model.ts +++ b/src/features/localization/models/brand-dictionary.model.ts @@ -3,19 +3,24 @@ interface ToolsMenuItem { url: string, } export interface BrandMenuItem { - type: "COPY" | "DOWNLOAD"; - icon: string; + copyLabel: string, + downloadLabel: string, +} +interface BrandMenuSection { label: string; - assetSrc: string; + svg: BrandMenuItem, + symbol: BrandMenuItem, + wordmark: BrandMenuItem, } -interface MenuSection { + +interface ToolsMenuSection { label: string; - items: BrandMenuItem[] | ToolsMenuItem[]; + items: ToolsMenuItem[]; } interface BrandMenu { - brand: MenuSection; - tools: MenuSection; + brand: BrandMenuSection; + tools: ToolsMenuSection; } export interface BrandDictionaryModel { diff --git a/tsconfig.json b/tsconfig.json index b1701cf6..6930c019 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,7 +20,8 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@/public/*": ["./public/*"] } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], diff --git a/types/svg.d.ts b/types/svg.d.ts new file mode 100644 index 00000000..c284f7d6 --- /dev/null +++ b/types/svg.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg?raw" { + const content: string; + export default content; +} From 958578c45e79d7995aff965b4a8088908787e7e7 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Wed, 1 Oct 2025 09:05:10 -0500 Subject: [PATCH 3/9] add notification component --- .../components/context-menu/context-menu.tsx | 8 +--- .../notification/notification.component.tsx | 27 +++++++++++ .../notification/notification.module.scss | 46 +++++++++++++++++++ .../site-brand/site-brand.component.tsx | 7 ++- .../localization/dictionaries/images/en.tsx | 1 + .../localization/dictionaries/images/ja.tsx | 1 + .../models/brand-dictionary.model.ts | 1 + 7 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 src/features/common/components/notification/notification.component.tsx create mode 100644 src/features/common/components/notification/notification.module.scss diff --git a/src/features/common/components/context-menu/context-menu.tsx b/src/features/common/components/context-menu/context-menu.tsx index 0be5e8dc..0b7108b0 100644 --- a/src/features/common/components/context-menu/context-menu.tsx +++ b/src/features/common/components/context-menu/context-menu.tsx @@ -10,10 +10,10 @@ import jwtWordmark from "@/public/img/jwt-wordmark.svg?raw"; interface ContextMenuProps { dictionary: BrandDictionaryModel; position: { x: number; y: number } | null; + setIsCopied: (value: React.SetStateAction) => void; } -const ContextMenu: FC = ({ dictionary, position }) => { - const [isCopied, setIsCopied] = useState(false); +const ContextMenu: FC = ({ dictionary, position, setIsCopied }) => { if (!position) return null; const handleIconCopy = async (svgString: string) => { @@ -24,10 +24,6 @@ const ContextMenu: FC = ({ dictionary, position }) => { try { await navigator.clipboard.writeText(svgString); setIsCopied(true); - - setTimeout(() => { - setIsCopied(false); - }, 2000); } catch (err) { console.error("Failed to copy SVG: ", err); } diff --git a/src/features/common/components/notification/notification.component.tsx b/src/features/common/components/notification/notification.component.tsx new file mode 100644 index 00000000..ca34ef0f --- /dev/null +++ b/src/features/common/components/notification/notification.component.tsx @@ -0,0 +1,27 @@ +import { useEffect } from 'react'; +import { createPortal } from 'react-dom'; +import styles from './notification.module.scss'; + +interface NotificationProps { + message: string; + onClose: () => void; +} + +const Notification = ({ message, onClose }: NotificationProps) => { + useEffect(() => { + const timer = setTimeout(() => { + onClose(); + }, 2000); + + return () => clearTimeout(timer); + }, [onClose]); + + return createPortal( +
+ {message} +
, + document.body + ); +}; + +export default Notification; \ No newline at end of file diff --git a/src/features/common/components/notification/notification.module.scss b/src/features/common/components/notification/notification.module.scss new file mode 100644 index 00000000..78423762 --- /dev/null +++ b/src/features/common/components/notification/notification.module.scss @@ -0,0 +1,46 @@ +.container { + position: fixed; + top: 1rem; + right: 1rem; + transform: translateY(0); + padding: 0.75rem 1rem; + background-color: var(--color_bg_layer_alternate-bold); + color: var(--color_fg_bold); + border-radius: 0.75rem; + font-size: 0.875rem; + font-weight: 500; + z-index: 9010; + animation: + fadeIn 0.3s, + fadeOut 0.3s 1.7s; + box-shadow: + 0 0 0 1px rgba(0, 0, 0, 0.12), + 0 2px 2px -1px rgba(0, 0, 0, 0.04), + 0 4px 4px -2px rgba(0, 0, 0, 0.04), + 0 8px 8px -4px rgba(0, 0, 0, 0.04), + 0 16px 16px -8px rgba(0, 0, 0, 0.04), + 0 32px 32px -16px rgba(0, 0, 0, 0.04), + inset 0 -1px 1px -0.5px rgba(0, 0, 0, 0.04); +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-100%); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeOut { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(-100%); + } +} diff --git a/src/features/common/components/site-brand/site-brand.component.tsx b/src/features/common/components/site-brand/site-brand.component.tsx index 184a8cda..d6302de5 100644 --- a/src/features/common/components/site-brand/site-brand.component.tsx +++ b/src/features/common/components/site-brand/site-brand.component.tsx @@ -7,6 +7,7 @@ import clsx from "clsx"; import { JwtLogoComponent } from "../../assets/jwt-logo.component"; import { JwtWordmarkComponent } from "../../assets/jwt-wordmark.component"; import ContextMenu from "../context-menu/context-menu"; +import Notification from "../notification/notification.component"; interface SiteBrandComponentProps extends PropsWithChildren { path: string; @@ -17,6 +18,7 @@ export const SiteBrandComponent: React.FC = ({ path, languageCode, }) => { + const [isCopied, setIsCopied] = useState(false); const [isTooltipVisible, setIsTooltipVisible] = useState(false); const [menuPosition, setMenuPosition] = useState<{ x: number; @@ -65,7 +67,10 @@ export const SiteBrandComponent: React.FC = ({
{brandDictionary.tooltip}
)} - + + {isCopied && ( + setIsCopied(false)}/> + )}
); }; diff --git a/src/features/localization/dictionaries/images/en.tsx b/src/features/localization/dictionaries/images/en.tsx index 1fbbbb7f..e7829e9d 100644 --- a/src/features/localization/dictionaries/images/en.tsx +++ b/src/features/localization/dictionaries/images/en.tsx @@ -1,6 +1,7 @@ import { BrandDictionaryModel } from "../../models/brand-dictionary.model"; export const enBrandDictionary: BrandDictionaryModel = { + alertMessage: "SVG copied to clipboard", tooltip: "Right-click or long-press for logo options", menu: { brand: { diff --git a/src/features/localization/dictionaries/images/ja.tsx b/src/features/localization/dictionaries/images/ja.tsx index ffc6a7e4..887bbe5d 100644 --- a/src/features/localization/dictionaries/images/ja.tsx +++ b/src/features/localization/dictionaries/images/ja.tsx @@ -1,6 +1,7 @@ import { BrandDictionaryModel } from "../../models/brand-dictionary.model"; export const jaBrandDictionary: BrandDictionaryModel = { + alertMessage: "SVG copied to clipboard", tooltip: "Right-click or long-press for logo options", menu: { brand: { diff --git a/src/features/localization/models/brand-dictionary.model.ts b/src/features/localization/models/brand-dictionary.model.ts index 912d4b36..4dfcce35 100644 --- a/src/features/localization/models/brand-dictionary.model.ts +++ b/src/features/localization/models/brand-dictionary.model.ts @@ -25,5 +25,6 @@ interface BrandMenu { export interface BrandDictionaryModel { tooltip: string; + alertMessage: string; menu: BrandMenu; } From 76ffd99563a1b3d4f7acdde9240b214a6d9d19e2 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Wed, 1 Oct 2025 13:26:44 -0500 Subject: [PATCH 4/9] add download buttons --- .../components/context-menu/context-menu.tsx | 57 ++++++++++++++----- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/src/features/common/components/context-menu/context-menu.tsx b/src/features/common/components/context-menu/context-menu.tsx index 0b7108b0..3fe2cd41 100644 --- a/src/features/common/components/context-menu/context-menu.tsx +++ b/src/features/common/components/context-menu/context-menu.tsx @@ -1,11 +1,12 @@ "use client"; -import React, { FC, useState } from "react"; +import React, { FC } from "react"; import { BrandDictionaryModel } from "@/features/localization/models/brand-dictionary.model"; import styles from "./context-menu.module.scss"; import jwtLogoString from "@/public/img/jwt-logo.svg?raw"; import jwtSymbolString from "@/public/img/jwt-symbol.svg?raw"; -import jwtWordmark from "@/public/img/jwt-wordmark.svg?raw"; +import jwtWordmarkString from "@/public/img/jwt-wordmark.svg?raw"; +import { Button } from "react-aria-components"; interface ContextMenuProps { dictionary: BrandDictionaryModel; @@ -13,7 +14,11 @@ interface ContextMenuProps { setIsCopied: (value: React.SetStateAction) => void; } -const ContextMenu: FC = ({ dictionary, position, setIsCopied }) => { +const ContextMenu: FC = ({ + dictionary, + position, + setIsCopied, +}) => { if (!position) return null; const handleIconCopy = async (svgString: string) => { @@ -35,29 +40,53 @@ const ContextMenu: FC = ({ dictionary, position, setIsCopied } style={{ top: position.y, left: position.x }} >
{dictionary.menu.brand.label}
- + + +
icon
+ {dictionary.menu.brand.wordmark.downloadLabel} +
+ ); }; From 62fbefd6cc5417d866c2711646c11af43aa82f34 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Wed, 1 Oct 2025 13:31:45 -0500 Subject: [PATCH 5/9] add tools links --- src/features/common/components/context-menu/context-menu.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/features/common/components/context-menu/context-menu.tsx b/src/features/common/components/context-menu/context-menu.tsx index 3fe2cd41..205101f4 100644 --- a/src/features/common/components/context-menu/context-menu.tsx +++ b/src/features/common/components/context-menu/context-menu.tsx @@ -7,6 +7,7 @@ import jwtLogoString from "@/public/img/jwt-logo.svg?raw"; import jwtSymbolString from "@/public/img/jwt-symbol.svg?raw"; import jwtWordmarkString from "@/public/img/jwt-wordmark.svg?raw"; import { Button } from "react-aria-components"; +import { a } from "vitest/dist/suite-dWqIFb_-.js"; interface ContextMenuProps { dictionary: BrandDictionaryModel; @@ -87,6 +88,10 @@ const ContextMenu: FC = ({ {dictionary.menu.brand.wordmark.downloadLabel} +
{dictionary.menu.tools.label}
+ {dictionary.menu.tools.items.map((el) => ( + {el.label} + ))} ); }; From 75054ebbf6806be9c8d72739c393cf242ca91574 Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:05:33 -0500 Subject: [PATCH 6/9] fix notification animation --- .../components/notification/notification.module.scss | 4 ++-- .../common/components/site-brand/site-brand.component.tsx | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/features/common/components/notification/notification.module.scss b/src/features/common/components/notification/notification.module.scss index 78423762..d3bf4a0a 100644 --- a/src/features/common/components/notification/notification.module.scss +++ b/src/features/common/components/notification/notification.module.scss @@ -11,8 +11,8 @@ font-weight: 500; z-index: 9010; animation: - fadeIn 0.3s, - fadeOut 0.3s 1.7s; + fadeIn 0.3s forwards, + fadeOut 0.3s 1.7s forwards; box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12), 0 2px 2px -1px rgba(0, 0, 0, 0.04), diff --git a/src/features/common/components/site-brand/site-brand.component.tsx b/src/features/common/components/site-brand/site-brand.component.tsx index d6302de5..1c1567d9 100644 --- a/src/features/common/components/site-brand/site-brand.component.tsx +++ b/src/features/common/components/site-brand/site-brand.component.tsx @@ -1,4 +1,4 @@ -import React, { PropsWithChildren, useEffect, useState } from "react"; +import React, { PropsWithChildren, useCallback, useEffect, useState } from "react"; import styles from "./site-brand.module.scss"; import Link from "next/link"; import { getBrandDictionary } from "@/features/localization/services/brand-dictionary.service"; @@ -34,6 +34,10 @@ export const SiteBrandComponent: React.FC = ({ setMenuPosition(null); }; + const handleNotificationClose = useCallback(() => { + setIsCopied(false); + }, []) + useEffect(() => { // Hide the menu on any click on the document document.addEventListener("click", handleCloseMenu); @@ -69,7 +73,7 @@ export const SiteBrandComponent: React.FC = ({ {isCopied && ( - setIsCopied(false)}/> + )} ); From ede47e60e13792b9f0bdca177c5ecaef07d2ebdf Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Wed, 1 Oct 2025 23:58:33 -0500 Subject: [PATCH 7/9] add icons and separator --- .../common/assets/download-icon.component.tsx | 37 ++++++++----------- .../context-menu/context-menu.module.scss | 7 ++++ .../components/context-menu/context-menu.tsx | 16 ++++---- .../components/icons/copy/copy-icon.tsx | 23 ++++++++++++ .../theme-picker/theme-picker.component.tsx | 1 - 5 files changed, 54 insertions(+), 30 deletions(-) create mode 100644 src/features/common/components/icons/copy/copy-icon.tsx diff --git a/src/features/common/assets/download-icon.component.tsx b/src/features/common/assets/download-icon.component.tsx index c2a92fda..c7eb7ec2 100644 --- a/src/features/common/assets/download-icon.component.tsx +++ b/src/features/common/assets/download-icon.component.tsx @@ -3,33 +3,26 @@ import React from "react"; export const DownloadIconComponent: React.FC = () => { return ( + d="M8 10V2M8 10L11 7M8 10L5 7" + stroke="currentColor" + stroke-width="1.5" + stroke-linecap="round" + stroke-linejoin="round" + > - + d="M1 11V12C1 13.6569 2.34315 15 4 15H12C13.6569 15 15 13.6569 15 12V11" + stroke="currentColor" + stroke-width="1.5" + stroke-linecap="round" + stroke-linejoin="round" + > ); }; diff --git a/src/features/common/components/context-menu/context-menu.module.scss b/src/features/common/components/context-menu/context-menu.module.scss index 2509ca3c..8bfe011b 100644 --- a/src/features/common/components/context-menu/context-menu.module.scss +++ b/src/features/common/components/context-menu/context-menu.module.scss @@ -52,3 +52,10 @@ background-color: var(--color_bg_layer_alternate-bold); } } + +.separator { + width: 100%; + height: 1px; + background-color: var(--color_border_default); + margin: 0.25rem 0; +} diff --git a/src/features/common/components/context-menu/context-menu.tsx b/src/features/common/components/context-menu/context-menu.tsx index 205101f4..8ecb97ba 100644 --- a/src/features/common/components/context-menu/context-menu.tsx +++ b/src/features/common/components/context-menu/context-menu.tsx @@ -7,7 +7,8 @@ import jwtLogoString from "@/public/img/jwt-logo.svg?raw"; import jwtSymbolString from "@/public/img/jwt-symbol.svg?raw"; import jwtWordmarkString from "@/public/img/jwt-wordmark.svg?raw"; import { Button } from "react-aria-components"; -import { a } from "vitest/dist/suite-dWqIFb_-.js"; +import { CopyIcon } from "../icons/copy/copy-icon"; +import { DownloadIconComponent } from "../../assets/download-icon.component"; interface ContextMenuProps { dictionary: BrandDictionaryModel; @@ -46,7 +47,7 @@ const ContextMenu: FC = ({ className={styles.menuItem} onPress={() => handleIconCopy(jwtLogoString)} > -
icon
+ {dictionary.menu.brand.svg.copyLabel} = ({ href="/img/jwt-logo.svg" download="jwt-logo.svg" > -
icon
+ {dictionary.menu.brand.svg.downloadLabel}
= ({ href="/img/jwt-symbol.svg" download="jwt-symbol.svg" > -
icon
+ {dictionary.menu.brand.symbol.downloadLabel}
= ({ href="/img/jwt-wordmark.svg" download="jwt-wordmark.svg" > -
icon
+ {dictionary.menu.brand.wordmark.downloadLabel}
+
{dictionary.menu.tools.label}
{dictionary.menu.tools.items.map((el) => ( {el.label} diff --git a/src/features/common/components/icons/copy/copy-icon.tsx b/src/features/common/components/icons/copy/copy-icon.tsx new file mode 100644 index 00000000..93a104a9 --- /dev/null +++ b/src/features/common/components/icons/copy/copy-icon.tsx @@ -0,0 +1,23 @@ +export const CopyIcon = () => { + return ( + + + + + ); +}; \ No newline at end of file diff --git a/src/features/common/components/theme-picker/theme-picker.component.tsx b/src/features/common/components/theme-picker/theme-picker.component.tsx index 2981e145..b4a74b54 100644 --- a/src/features/common/components/theme-picker/theme-picker.component.tsx +++ b/src/features/common/components/theme-picker/theme-picker.component.tsx @@ -27,7 +27,6 @@ export const ThemePickerComponent: React.FC = ({ data-active={selectedOptionCode === option.code} title={option.label} onClick={async () => { - console.log(option.label) await handleSelection(option.code); }} > From 908bdda0c20b7325b7618c09d13a2a43f05f9b2f Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Thu, 2 Oct 2025 00:55:36 -0500 Subject: [PATCH 8/9] add navbar hover states --- .../common/assets/jwt-logo.component.tsx | 8 ++-- .../common/assets/jwt-wordmark.component.tsx | 12 ++--- .../ribbon/assets/dark-icon.component.tsx | 22 ++++----- .../ribbon/assets/light-icon.component.tsx | 43 +++++++++-------- .../ribbon/assets/system-icon.component.tsx | 47 +++++++++++++------ .../headers/header/header.module.scss | 20 +++++--- .../site-brand/site-brand.module.scss | 1 + .../theme-picker/theme-picker.module.scss | 6 ++- src/libs/theme/styles/globals.scss | 8 ++-- 9 files changed, 100 insertions(+), 67 deletions(-) diff --git a/src/features/common/assets/jwt-logo.component.tsx b/src/features/common/assets/jwt-logo.component.tsx index 57db4704..540dc371 100644 --- a/src/features/common/assets/jwt-logo.component.tsx +++ b/src/features/common/assets/jwt-logo.component.tsx @@ -11,15 +11,15 @@ export const JwtLogoComponent: React.FC = () => { fillRule="evenodd" clipRule="evenodd" d="M18.3683 8.60806V0H13.5682V8.60806L15.9683 11.9041L18.3683 8.60806Z" - fill="#191919" - style={{ fill: '#191919', fillOpacity: 1 }} + fill="currentColor" + style={{ fillOpacity: 1 }} /> { > diff --git a/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx index 1f7d4251..7607d241 100644 --- a/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx @@ -3,20 +3,18 @@ import React from "react"; export const DarkIconComponent: React.FC = () => { return ( - + ); }; diff --git a/src/features/common/components/bars/ribbon/assets/light-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/light-icon.component.tsx index 86bb0bab..d1f78402 100644 --- a/src/features/common/components/bars/ribbon/assets/light-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/light-icon.component.tsx @@ -3,28 +3,31 @@ import React from "react"; export const LightIconComponent: React.FC = () => { return ( - - - - - - - - - + + + ); }; diff --git a/src/features/common/components/bars/ribbon/assets/system-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/system-icon.component.tsx index 27be5261..dbc0ed77 100644 --- a/src/features/common/components/bars/ribbon/assets/system-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/system-icon.component.tsx @@ -3,22 +3,41 @@ import React from "react"; export const SystemIconComponent: React.FC = () => { return ( - - - + + + + ); }; diff --git a/src/features/common/components/headers/header/header.module.scss b/src/features/common/components/headers/header/header.module.scss index 8843eb04..9a00d9dc 100644 --- a/src/features/common/components/headers/header/header.module.scss +++ b/src/features/common/components/headers/header/header.module.scss @@ -14,7 +14,7 @@ box-sizing: border-box; background: var(--color_bg_app_bar); border: 1px solid var(--color_bg_app_bar); - box-shadow: 0 12px 24px -12px rgba(0, 0, 0, .04); + box-shadow: 0 12px 24px -12px rgba(0, 0, 0, 0.04); backdrop-filter: blur(3rem); z-index: 100; @@ -62,7 +62,7 @@ align-items: center; justify-content: center; border-radius: 9999px; - padding: .25rem; + padding: 0.25rem; } .navList { @@ -89,13 +89,13 @@ .navList__item > a { display: flex; align-items: center; - padding: .5rem 1rem; + padding: 0.5rem 1rem; color: var(--color_fg_default); - font-size: .875rem; + font-size: 0.875rem; font-style: normal; font-weight: 600; line-height: 1.5; - letter-spacing: -.1px; + letter-spacing: -0.1px; border-radius: 9999px; &:focus-visible { @@ -103,14 +103,20 @@ outline-offset: 0.125rem; border-radius: 0.125rem; } + + &:hover { + background-color: var(--color_bg_app_bar_border); + color: var(--color_fg_bold); + transition: all 0.1s ease-out; + } } .actions { display: flex; - gap: .5rem; + gap: 0.5rem; @media #{$breakpoint-dimension-md} { gap: 1rem; margin-right: 1.5rem; } -} \ No newline at end of file +} diff --git a/src/features/common/components/site-brand/site-brand.module.scss b/src/features/common/components/site-brand/site-brand.module.scss index 4b03b0ad..dadc015d 100644 --- a/src/features/common/components/site-brand/site-brand.module.scss +++ b/src/features/common/components/site-brand/site-brand.module.scss @@ -6,6 +6,7 @@ gap: 0.5rem; position: relative; cursor: pointer; + color: var(--color_fg_bold); svg { height: inherit; diff --git a/src/features/common/components/theme-picker/theme-picker.module.scss b/src/features/common/components/theme-picker/theme-picker.module.scss index 90049ed9..f8640e68 100644 --- a/src/features/common/components/theme-picker/theme-picker.module.scss +++ b/src/features/common/components/theme-picker/theme-picker.module.scss @@ -32,7 +32,11 @@ &[data-active="true"] { background-color: var(--color_bg_layer); color: var(--color_fg_bold); - box-shadow: 0 1px 2px rgba(0, 0, 0, .1); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + } + + &:hover { + background-color: var(--color_bg_layer_alternate-bold); } } diff --git a/src/libs/theme/styles/globals.scss b/src/libs/theme/styles/globals.scss index 7915f0b2..ef47609e 100644 --- a/src/libs/theme/styles/globals.scss +++ b/src/libs/theme/styles/globals.scss @@ -48,8 +48,8 @@ --color_fg_default: var(--functional-gray-200); --color_fg_link_primary: var(--cloud); - --color_bg_app_bar: hsla(0,0%,7%,.04); - --color_bg_app_bar_border: hsla(0, 0%, 7%, .12); + --color_bg_app_bar: hsla(0, 0%, 95%, 0.04); + --color_bg_app_bar_border: hsla(0, 0%, 95%, 0.12); --color_bg_app_bar_plain: rgba(17, 17, 17); --color_fg_link: var(--functional-gray-50); @@ -66,7 +66,7 @@ --color_bg_layer_bold: var(--functional-gray-700); --color_bg_layer_boldest: var(--functional-gray-50); --color_bg_layer_alternate: var(--functional-gray-850); - --color_bg_layer_alternate-bold: var(--functional-gray-800); + --color_bg_layer_alternate-bold: var(--functional-gray-850); --color_bg_layer_elevated: var(--functional-gray-900); --color_bg_layer_highlight1: var(--ocean); @@ -198,7 +198,9 @@ html[data-theme="system-light"] { --color_fg_bold: var(--charcoal2); --color_fg_link_primary: var(--sky); + --color_bg_app_bar: hsla(0, 0%, 7%, 0.04); --color_bg_app_bar_plain: rgba(241, 241, 241); + --color_bg_app_bar_border: hsla(0, 0%, 7%, 0.12); --color_fg_link: var(--charcoal2); --color_fg_link_hover: var(--charcoal2); From d46620e5f6d759e3b9872ea6e7e505a106951e9f Mon Sep 17 00:00:00 2001 From: Christian Samaniego <199278128+christiansamaniego-okta@users.noreply.github.com> Date: Thu, 2 Oct 2025 09:59:48 -0500 Subject: [PATCH 9/9] remove vscode settings --- .vscode/settings.json | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 7a73a41b..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file