diff --git a/.nvmrc b/.nvmrc index 7fd023741..67d2ffed5 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16.15.0 +v20.13.1 \ No newline at end of file diff --git a/.tool-versions b/.tool-versions index eedff58c8..ac4d6020f 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 16.16.0 +nodejs 18.14.1 diff --git a/README.md b/README.md index ab7109a62..1b2c88e5b 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,24 @@ npm install && npm run dev - All articles are markdown and stored in `/src/content/docs/`. - Navigation is JSON in `/src/config/sidebar.ts` +## Tooling on Scroll + +If you'd like to add an entry to our [tooling list](http://docs.scroll.xyz/en/developers/scroll-contracts), create a PR to add a new `mdx` file in the [tooling content folder](src/content/tools), using the following template. You can also refer to other existing entries for reference. + +``` +--- +name: "Safe" +category: ["Identity", "Wallet"] +excerpt: "Safe allows you to create smart wallet on chain." +logo: { src: "https://app.safe.global/images/safe-logo-green.png", alt: "Safe Logo" } +website: "https://app.safe.global" +network: ["Mainnet", "Testnet] +noAdditionalInfo: false +--- + +Add additional info here about how to access this tool on Scroll (ex. contract addresses, tutorials, API URLs) +``` + ## Credits - Special thanks to the Chainlink team whose documentation we forked. Their repo is available [here](https://github.com/smartcontractkit/documentation) and viewable at [https://docs.chain.link/](https://docs.chain.link/). diff --git a/astro-i18next.config.ts b/astro-i18next.config.ts index 2b3e6b322..5f1c032fa 100644 --- a/astro-i18next.config.ts +++ b/astro-i18next.config.ts @@ -2,6 +2,6 @@ export default { defaultLocale: "en", showDefaultLocale: true, - locales: ["en", "zh", "es"], + locales: ["en", "es", "zh", "tr"], load: ["server", "client"], } diff --git a/astro.config.ts b/astro.config.ts index 61447a66c..438c0cc51 100644 --- a/astro.config.ts +++ b/astro.config.ts @@ -1,10 +1,10 @@ import { defineConfig } from "astro/config" import preact from "@astrojs/preact" import react from "@astrojs/react" +import svgr from "vite-plugin-svgr" import astroI18next from "astro-i18next" import { astroCallouts, asideAutoImport } from "./integrations/astro-callouts" import { solidityRemixCode, codeSampleAutoImport } from "./integrations/solidity-remix" -import { youtubeEmbed } from "./integrations/youtube-embed" import mdx from "@astrojs/mdx" import rehypeSlug from "rehype-slug" import rehypeAutolinkHeadings from "rehype-autolink-headings" @@ -12,16 +12,18 @@ import rehypeKatex from "rehype-katex" import rehypeMermaid from "rehype-mermaidjs" import remarkGfm from "remark-gfm" import remarkMath from "remark-math" -import image from "@astrojs/image" import AutoImport from "astro-auto-import" import sitemap from "@astrojs/sitemap" import tailwind from "@astrojs/tailwind" +import expressiveCode from "astro-expressive-code" + // https://astro.build/config export default defineConfig({ site: "https://docs.scroll.io", + scopedStyleStrategy: "where", legacy: { astroFlavoredMarkdown: true, }, @@ -32,21 +34,38 @@ export default defineConfig({ preact({ compat: true, }), + sitemap({ changefreq: "daily", }), astroCallouts(), solidityRemixCode(), - youtubeEmbed(), + expressiveCode({ + themes: ["dark-plus"], + defaultProps: { + frame: "code", + }, + styleOverrides: { + borderRadius: "27px", + borderColor: "transparent", + frames: { + shadowColor: "transparent", + editorTabBorderRadius: "0.5rem", + editorBackground: "#2b2b2b", + }, + }, + }), mdx(), - image(), tailwind({ - // Example: Disable injecting a basic `base.css` import on every page. - // Useful if you need to define and/or import your own custom `base.css`. - config: { applyBaseStyles: false }, + applyBaseStyles: false, + nesting: true, }), + astroI18next(), ], + vite: { + plugins: [svgr()], + }, markdown: { drafts: true, remarkPlugins: [remarkMath, remarkGfm], @@ -56,7 +75,14 @@ export default defineConfig({ [ rehypeAutolinkHeadings, { - behavior: "append", + behavior: "wrap", + properties: {}, + content: { + type: "element", + tagName: "span", + properties: { className: ["icon", "icon-link"] }, + children: [], + }, }, ], [ diff --git a/integrations/utils/makeComponentNode.ts b/integrations/utils/makeComponentNode.ts index c624d909f..04b9b10d8 100644 --- a/integrations/utils/makeComponentNode.ts +++ b/integrations/utils/makeComponentNode.ts @@ -21,7 +21,7 @@ export function makeComponentNode( type: "mdxJsxFlowElement", name, attributes: Object.entries(attributes) - // Filter out non-truthy attributes to avoid empty attrs being parsed as `true`. + // Filter out non-truthy attributes to avoid empty attributes being parsed as `true`. .filter(([_k, v]) => v !== false && Boolean(v)) .map(([name, value]) => ({ type: "mdxJsxAttribute", diff --git a/integrations/youtube-embed.ts b/integrations/youtube-embed.ts deleted file mode 100644 index 85b3f191a..000000000 --- a/integrations/youtube-embed.ts +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Add Youtube import to MD pages. - * Remove when astro supports 3rd party integration officially. - */ - -import type { AstroIntegration } from "astro" - -const CodeSampleTagName = "YouTube" -/** - * Astro integration that sets up the remark plugin and auto-imports the ` @@ -36,11 +42,8 @@ const icons = { /* Indicates the aside boundaries for forced colors users, transparent is changed to a solid color */ outline: 1px solid transparent; - --aside-bg: var(--color-background-info); - background-color: var(--aside-bg); - /* display: flex; */ - @apply p-[30px] gap-4 rounded-[27px] text-info mb-[1rem]; + @apply p-[30px] gap-4 rounded-[27px] text-info mb-[1rem] bg-callout-note dark:bg-callout-dark-note; } aside p.title { @@ -82,19 +85,23 @@ const icons = { } .tip { - --aside-bg: #eee; + @apply bg-callout-tip dark:bg-callout-dark-tip; } .caution { - --aside-bg: var(--color-background-warning); + @apply bg-callout-caution dark:bg-callout-dark-caution; } .danger { - --aside-bg: var(--color-background-error); + @apply bg-callout-danger dark:bg-callout-dark-danger; + } + + .note { + @apply bg-callout-note dark:bg-callout-dark-note; } .asideContent :global(p) { - margin-bottom: 0; + margin-bottom: 0 !important; font-size: 16px; line-height: 25px; } diff --git a/src/components/ClickToZoom.astro b/src/components/ClickToZoom.astro index 72d00d5ad..3e7eb6ff5 100644 --- a/src/components/ClickToZoom.astro +++ b/src/components/ClickToZoom.astro @@ -1,5 +1,5 @@ --- -import { Image } from "@astrojs/image/components" +import { Image } from "astro:assets" export type Props = { src: string alt: string diff --git a/src/components/Columns.astro b/src/components/Columns.astro new file mode 100644 index 000000000..8996d25fc --- /dev/null +++ b/src/components/Columns.astro @@ -0,0 +1,16 @@ +--- +// Display info in two columns +--- + +
+
+
+
+ + diff --git a/src/components/Footer/Footer.astro b/src/components/Footer/Footer.astro new file mode 100644 index 000000000..5feac5c91 --- /dev/null +++ b/src/components/Footer/Footer.astro @@ -0,0 +1,10 @@ +--- +import Subscribe from "./Subscribe/Subscribe.tsx" +import PureFooter from "./PureFooter/PureFooter.tsx" +import i18next from "i18next" +--- + +
+ + +
diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx deleted file mode 100644 index 9c6b422b7..000000000 --- a/src/components/Footer/Footer.tsx +++ /dev/null @@ -1,15 +0,0 @@ -// import PureFooter from "./PureFooter" -import React, { useState, useCallback } from "react" -import Subscribe from "./Subscribe/Subscribe.tsx" -import PureFooter from "./PureFooter/PureFooter.tsx" - -const Footer = () => { - return ( - <> - - - - ) -} - -export default Footer diff --git a/src/components/Footer/PureFooter/PureFooter.tsx b/src/components/Footer/PureFooter/PureFooter.tsx index ee126d83f..6ce32e8b2 100644 --- a/src/components/Footer/PureFooter/PureFooter.tsx +++ b/src/components/Footer/PureFooter/PureFooter.tsx @@ -1,6 +1,7 @@ import { useState, useEffect } from "preact/hooks" import { aboutList, mediaList, resourceList } from "../helper.tsx" import styles from "./PureFooter.module.css" +import { t } from "i18next" const Footer = () => { return ( @@ -9,28 +10,28 @@ const Footer = () => {
-

About Scroll

+

{ t("footer.aboutScroll.title") }

-

Resources

+

{ t("footer.resources.title") }

-

Follow Us

+

{ t("footer.followUs.title") }

{mediaList.map((item) => ( @@ -39,8 +40,6 @@ const Footer = () => { ))}
- -

© Version 1.0.0 Scroll Ltd 2023

) } diff --git a/src/components/Footer/Subscribe/EmailInput.module.css b/src/components/Footer/Subscribe/EmailInput.module.css index 5cb898d2b..107b40bc4 100644 --- a/src/components/Footer/Subscribe/EmailInput.module.css +++ b/src/components/Footer/Subscribe/EmailInput.module.css @@ -11,7 +11,6 @@ width: 48px; height: 100%; position: absolute; - background-color: #1f1f1f; border-radius: 10px; z-index: 1; top: 0; @@ -19,12 +18,12 @@ transition: width 0.5s ease; overflow: hidden; cursor: default; + @apply bg-black; } .iconButton { width: 54px; height: 100%; - color: #fff; display: inline-flex; align-items: center; justify-content: center; @@ -47,6 +46,7 @@ border-radius: 50%; overflow: visible; transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + @apply text-white; } .iconButton img { @@ -57,13 +57,12 @@ .success { font-size: 16px; font-weight: 600; - color: #fff; - background-color: #1f1f1f; border-radius: 10px; border-left: none; text-align: center; flex: 1; line-height: 54px; + @apply text-white bg-black; } .inputBase { @@ -71,16 +70,11 @@ font-weight: 600; height: 100%; width: 100%; - color: #000; - background-color: #fff; border-radius: 10px; border: 1px solid #000; border-left: none; text-align: center; -} - -.inputBase::placeholder { - color: #dcdcdc; + @apply text-black bg-pure-white border-black; } @media (min-width: 50em) { diff --git a/src/components/Footer/Subscribe/EmailInput.tsx b/src/components/Footer/Subscribe/EmailInput.tsx index b3e586002..92fff969a 100644 --- a/src/components/Footer/Subscribe/EmailInput.tsx +++ b/src/components/Footer/Subscribe/EmailInput.tsx @@ -1,5 +1,8 @@ -import React, { useState } from "react" +import React from "react" import styles from "./EmailInput.module.css" +import ArrowSvg from "~/assets/svgs/footer/arrow-right.svg?react" +import { clsx } from "~/lib" +import { t } from "i18next" const EmailInput = (props) => { const { end, onClick, onEnter, ...restProps } = props @@ -13,21 +16,29 @@ const EmailInput = (props) => { return (
- -
Thank you for subscribing!
+
{ t("landing.NewsletterCTA.thankYouForSubscribing") }
) diff --git a/src/components/Footer/Subscribe/Subscribe.tsx b/src/components/Footer/Subscribe/Subscribe.tsx index 3ec79e912..b3ff1e221 100644 --- a/src/components/Footer/Subscribe/Subscribe.tsx +++ b/src/components/Footer/Subscribe/Subscribe.tsx @@ -1,5 +1,9 @@ +// import React from "react" import { useState, useEffect } from "preact/hooks" import MailchimpSubscribe from "react-mailchimp-subscribe" +import SubscribeSvg from "~/assets/svgs/footer/subscribe.svg?react" +import { clsx } from "~/lib" +import i18next, { changeLanguage, t } from "i18next" import EmailInput from "./EmailInput.tsx" import styles from "./Subscribe.module.css" @@ -7,15 +11,17 @@ import styles from "./Subscribe.module.css" const url = "https://gmail.us14.list-manage.com/subscribe/post?u=3b1d822eb27b2fa64d82d430b&id=0b4603244e" const isValidEmail = (email: string): boolean => { - const emailRegex: RegExp = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ + const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ return emailRegex.test(email) } -export default function Subscribe() { +export default function Subscribe(props) { const [email, setEmail] = useState("") const [customMessage, setCustomMessage] = useState("") const [emailValid, setEmailValid] = useState(false) + i18next.changeLanguage(props.lang) + useEffect(() => { setCustomMessage("") setEmailValid(isValidEmail(email)) @@ -23,9 +29,9 @@ export default function Subscribe() { const handleSubmit = (subscribe) => { if (!email) { - setCustomMessage("Please insert your email.") + setCustomMessage(t("landing.NewsletterCTA.pleaseInsertEmail")) } else if (!emailValid) { - setCustomMessage("Please use a correct email address.") + setCustomMessage(t("landing.NewsletterCTA.correctEmail")) } else { subscribe({ EMAIL: email }) setEmail("") @@ -37,12 +43,17 @@ export default function Subscribe() { } return ( -
+
- + + + +
-
Stay up-to-date on the latest Scroll Developer news
-
Roadmap updates, virtual and live events, ecosystem opportunities and more
+
{ t("landing.NewsletterCTA.title") }
+
+ { t("landing.NewsletterCTA.text") } +
handleSubmit(subscribe)} onEnter={() => handleSubmit(subscribe)} - placeholder="your email address here" + placeholder= { t("landing.NewsletterCTA.placeholder") } end={status === "success"} /> {customMessage &&
{customMessage}
} - {status === "error" &&
{message}
} + {status === "error" &&
{t(message)}
}
)} /> diff --git a/src/components/Footer/helper.tsx b/src/components/Footer/helper.tsx index 19835b40f..e134b224e 100644 --- a/src/components/Footer/helper.tsx +++ b/src/components/Footer/helper.tsx @@ -23,35 +23,43 @@ const TwitterIcon: React.FC> = (props) => ( export const aboutList = [ { - name: "Join Us", - href: "https://jobs.lever.co/ScrollFoundation", + name: "footer.aboutScroll.bugBounty", + href: "https://immunefi.com/bounty/scroll/", }, { - name: "Health Status", + name: "footer.aboutScroll.joinUs", + href: "https://scroll.io/join-us", + }, + { + name: "footer.aboutScroll.healthStatus", href: "https://status.scroll.io/", }, { - name: "Privacy Policy", + name: "footer.aboutScroll.privacyPolicy", href: "https://scroll.io/privacy-policy", }, { - name: "Terms and Conditions", + name: "footer.aboutScroll.termsAndConditions", href: "https://scroll.io/terms-and-conditions", }, ] export const resourceList = [ { - name: "Blog", + name: "footer.resources.blog", href: "https://scroll.io/blog", }, { - name: "Documentation", + name: "footer.resources.documentation", href: "https://docs.scroll.io/", }, { - name: "Press Kit", - href: "https://scrollzkp.notion.site/Scroll-Rebrand-Assets-5bb83465f56f40989c4f772b39ed3a06", + name: "footer.resources.brandKit", + href: "https://scroll.io/brand-kit", + }, + { + name: "footer.resources.audits", + href: "https://github.com/scroll-tech/scroll-audits", }, ] diff --git a/src/components/HeadCommon.astro b/src/components/HeadCommon.astro index 29073a65b..f024f2799 100644 --- a/src/components/HeadCommon.astro +++ b/src/components/HeadCommon.astro @@ -3,7 +3,6 @@ import "../styles/theme.css" import "../styles/index.css" import "../styles/migrated.css" import "../styles/prism-darcula.css" -import "../styles/copy-to-clipboard.css" import "../styles/design-system/global-styles.css" --- @@ -12,7 +11,8 @@ import "../styles/design-system/global-styles.css" - + + diff --git a/src/components/Header/Header.astro b/src/components/Header/Header.astro index 580473af1..2f33c02ea 100644 --- a/src/components/Header/Header.astro +++ b/src/components/Header/Header.astro @@ -4,6 +4,8 @@ import { Frontmatter, MENU } from "../../config/index" import SkipToContent from "./SkipToContent.astro" import SidebarToggle from "./SidebarToggle.tsx" import Search from "./Search/Search.tsx" +import ThemeModeToggle from "./ThemeModeToggle" +import GithubSvg from "~/assets/svgs/header/github.svg?raw" // import LanguageSelector from "./LanguageSelector/index.tsx" import LanguageSelector from "./LanguageSelector/LanguageSelector.astro" @@ -11,6 +13,7 @@ import { localizePath } from "astro-i18next" import i18next from "i18next" import NotificationBanner from "~/features/notifications/components/NotificationBanner.astro" +import { clsx } from "~/lib" export type Props = { frontmatter?: Frontmatter @@ -20,7 +23,7 @@ const { frontmatter, dark } = Astro.props const backgroundColor = dark ? "#101010" : "#ffffff" -const textColor = dark ? "#FFF8F3" : "#101010" +const textColor = dark ? "#FFF8F3" : "" const section = frontmatter?.section ?? "" --- @@ -33,23 +36,26 @@ const section = frontmatter?.section ?? "" +
+ diff --git a/src/components/Header/LanguageSelector/LanguageSelector.astro b/src/components/Header/LanguageSelector/LanguageSelector.astro index e2fc38268..b950f43d4 100644 --- a/src/components/Header/LanguageSelector/LanguageSelector.astro +++ b/src/components/Header/LanguageSelector/LanguageSelector.astro @@ -3,93 +3,148 @@ import i18next from "i18next" import { localizePath } from "astro-i18next" import localeEmoji from "locale-emoji" import ISO6991 from "iso-639-1" +import { clsx } from "~/lib" const supportedLanguages = i18next.languages -const currentLanguage = i18next.language const { pathname } = Astro.url -const { showFlag = false, languageMapping, ...attributes } = Astro.props ---- +const { showFlag = false, languageMapping } = Astro.props +const { dark } = Astro.props -
-
    - { - supportedLanguages.map((supportedLanguage) => { - let value = localizePath(pathname, supportedLanguage) - const flag = showFlag ? localeEmoji(supportedLanguage) + " " : "" - let nativeName = "" - if (languageMapping && languageMapping.hasOwnProperty(supportedLanguage)) { - nativeName = languageMapping[supportedLanguage] - } else { - nativeName = ISO6991.getNativeName(supportedLanguage) - } +const getLabel = (language) => { + const flag = showFlag ? localeEmoji(language) + " " : "" + let nativeName = "" + if (languageMapping && languageMapping.hasOwnProperty(language)) { + nativeName = languageMapping[language] + } else { + nativeName = ISO6991.getNativeName(language) + } - const label = flag + nativeName + return flag + nativeName +} +--- - return ( -
  • - {label} -
  • - ) - }) - } -
+
+
+ + + + {getLabel(supportedLanguages[0])} +
+
+
    + { + supportedLanguages.map((supportedLanguage, idx) => { + let value = localizePath(pathname, supportedLanguage) + const label = getLabel(supportedLanguage) + return ( +
  • + {idx === 0 ? ( + + + + + ) : ( + + + + )} + {label} +
  • + ) + }) + } +
+
diff --git a/src/components/Header/ScrollLogo.tsx b/src/components/Header/ScrollLogo.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/Header/Search/Search.module.css b/src/components/Header/Search/Search.module.css index 49570f7df..59cb45200 100644 --- a/src/components/Header/Search/Search.module.css +++ b/src/components/Header/Search/Search.module.css @@ -1,12 +1,11 @@ /** Style Algolia */ #searchModal.mini { - background: #ffffff; position: fixed; top: 65px; left: 0; right: 0; - height: 100vh; + height: calc(100vh - 65px); padding-top: 60px; border-radius: 0; } @@ -14,8 +13,6 @@ #searchModal.large { top: 0; height: fit-content; - max-height: 80vh; - background: #fff; position: absolute; border-radius: 24px; padding-top: 0; @@ -24,7 +21,7 @@ .resultsWrapper { padding: 20px 20px 1rem; - overflow: overlay; + height: calc(100% - 50px); } .searchInput { @@ -40,12 +37,12 @@ outline: 0; cursor: pointer; align-items: center; - width: 346px; + width: 100%; height: 35px; border-radius: 40px; font-style: italic; - background: #f6f6f6; - @apply border-divider text-placeholder gap-2; + + @apply border-divider text-black gap-2 bg-[#f6f6f6]; } .searchInputMobile { @@ -61,7 +58,7 @@ .searchInput:hover, .searchInput:focus { - @apply text-text border-light; + @apply text-black border-light; } .searchInput::placeholder, @@ -119,7 +116,6 @@ .closeButtonMobile { display: block; - background-color: #ffffff; border: 3px solid #ffffff; padding: 0; font-size: 16px; @@ -127,11 +123,8 @@ position: relative; right: -43px; box-sizing: content-box; -} -.hitWrapper { - max-height: 40vh; - overflow: overlay; + @apply bg-pure-white; } .hitWrapper ol { @@ -141,12 +134,15 @@ .hit { line-height: 32px; display: block; - @apply text-primary mb-2; + @apply mb-2 text-black; } .queryResults { display: grid; grid-template-columns: 1fr; + grid-auto-rows: max-content; + height: 100%; + overflow-y: auto; } .queryResults h6 { @@ -177,6 +173,7 @@ .hitList mark { background-color: transparent; font-weight: bold; + @apply text-inherit; } .hideSm { @@ -198,16 +195,17 @@ .container { position: relative; + width: 100%; } #searchModal.mini { top: 0; height: fit-content; max-height: 80vh; - background: #f6f6f6; position: absolute; border-radius: 17.5px; padding-top: 0; + @apply bg-[#f6f6f6]; } #searchModal.large { @@ -224,8 +222,8 @@ } .hitWrapper { - max-height: calc(80vh - 100px); - overflow: auto; + max-height: calc(80vh - 150px); + overflow-y: auto; } .hitList li { @@ -246,9 +244,9 @@ } @media (max-width: 72em) and (min-width: 50em) { - :global(.right-box .tools) { + /* :global(.right-box .tools) { display: none; - } + } */ .queryResults { grid-template-columns: 1fr !important; @@ -259,4 +257,9 @@ .resultsWrapper * { padding-left: 0 !important; } + + .queryResults.large { + max-height: calc(80vh - 48px); + height: max-content; + } } diff --git a/src/components/Header/Search/Search.tsx b/src/components/Header/Search/Search.tsx index fa6d0ff3e..76d736187 100644 --- a/src/components/Header/Search/Search.tsx +++ b/src/components/Header/Search/Search.tsx @@ -3,6 +3,7 @@ import styles from "./Search.module.css" import { useKeyPress } from "~/hooks/useKeyPress" import { SearchModal } from "./SearchModal" +import { clsx } from "~/lib" export default function Search() { const [isOpen, setIsOpen] = useState(false) @@ -15,8 +16,14 @@ export default function Search() { const body = document.body if (isOpen) { body.classList.add("global-search-toggle") + if (window.matchMedia("(max-width: 50em)").matches) { + document.querySelector("#themeModeToggle").style.right = "-43px" + } } else { body.classList.remove("global-search-toggle") + if (window.matchMedia("(max-width: 50em)").matches) { + document.querySelector("#themeModeToggle").style.right = 0 + } } }, [isOpen]) @@ -34,13 +41,22 @@ export default function Search() { return (
-
+
@@ -52,21 +68,21 @@ export default function Search() { height="18" viewBox="0 0 18 18" fill="none" - className={styles.closeButtonMobile} + className={clsx(styles.closeButtonMobile, "dark:border-black dark:bg-black")} onClick={onClose} > ) : ( )}
diff --git a/src/components/Header/Search/SearchInput.module.css b/src/components/Header/Search/SearchInput.module.css index 87ab4a215..17c8ab48b 100644 --- a/src/components/Header/Search/SearchInput.module.css +++ b/src/components/Header/Search/SearchInput.module.css @@ -9,7 +9,6 @@ overflow: hidden; padding: 0 20px; border-radius: 0; - @apply bg-white; } .wrapper.large { @@ -21,7 +20,6 @@ margin: 0; font-size: 16px; width: 100%; - color: rgb(12, 22, 44); outline: none; display: block; appearance: none; @@ -34,6 +32,8 @@ border-radius: 0; height: 50px; line-height: 45px; + + @apply text-black border-black; } .input.large { height: 48px; @@ -77,7 +77,7 @@ .input { font-size: 16px; line-height: 15px; - background: #f6f6f6 url(/assets/search.svg) left 12px top 50% / 17px no-repeat; + background: url(/assets/search.svg) left 12px top 50% / 17px no-repeat; border: none; height: 35px; line-height: 35px; @@ -90,6 +90,7 @@ background: transparent url(/svgs/search.svg) left 22px center no-repeat; padding-left: 60px; padding-right: 22px; + background-size: 22px auto; } .input:focus { diff --git a/src/components/Header/Search/SearchInput.tsx b/src/components/Header/Search/SearchInput.tsx index c02a8a979..a7368b637 100644 --- a/src/components/Header/Search/SearchInput.tsx +++ b/src/components/Header/Search/SearchInput.tsx @@ -24,9 +24,24 @@ export const SearchInput = ({ size, onClose }: { onClose: () => void }) => { }, [debouncedValue]) return ( -
+
+
Recommended articles
@@ -46,7 +47,7 @@ function EmptyQueryBoundary({ children, fallback }) { {article.title} @@ -66,7 +67,7 @@ function EmptyQueryBoundary({ children, fallback }) { {article.title} @@ -108,6 +109,7 @@ function NoResultsBoundary({ children }) { function CustomHits({ title, hitClassName, ...props }: UseHitsProps & { title: string; hitClassName?: string }) { const { hits, results } = useHits(props) + console.log(hits, "hits") if (hits.length === 0) return null return (
@@ -119,7 +121,7 @@ function CustomHits({ title, hitClassName, ...props }: UseHitsProps & { title: s +
- + -
+
= ({ dark }) => { type="button" aria-pressed={sidebarShown ? "true" : "false"} id="menu-toggle" - className={clsx(styles.button, dark && styles.dark)} + className={clsx(styles.button, "text-black", dark && "text-white", "dark:text-white-800")} onClick={() => setSidebarShown(!sidebarShown)} > {sidebarShown ? ( diff --git a/src/components/Header/ThemeModeToggle.tsx b/src/components/Header/ThemeModeToggle.tsx new file mode 100644 index 000000000..777d63a61 --- /dev/null +++ b/src/components/Header/ThemeModeToggle.tsx @@ -0,0 +1,58 @@ +import { useEffect, useState } from "preact/hooks" +import useStorage from "squirrel-gill" +import MoonSvg from "~/assets/svgs/header/moon.svg?react" +import SunSvg from "~/assets/svgs/header/sun.svg?react" +import { clsx } from "~/lib" + +const ThemeModeToggle = (props) => { + const { dark } = props + const [themeMode, setThemeMode] = useStorage(localStorage, "THEME_MODE", "light") + const [isDarkMode, setIsDarkMode] = useState(false) + + useEffect(() => { + const toggleThemeMode = (e) => { + if (e.matches) { + setThemeMode("dark") + } else [setThemeMode("light")] + } + + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", toggleThemeMode) + + return () => { + window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", toggleThemeMode) + } + }, []) + + useEffect(() => { + if (themeMode === "dark") { + setIsDarkMode(true) + document.documentElement.classList.add("dark") + } else { + setIsDarkMode(false) + document.documentElement.classList.remove("dark") + } + }, [themeMode]) + + const handleToggleThemeMode = () => { + if (isDarkMode) { + setThemeMode("light") + } else { + setThemeMode("dark") + } + } + + return ( + + ) +} + +export default ThemeModeToggle diff --git a/src/components/Icons/Icons.astro b/src/components/Icons/Icons.astro new file mode 100644 index 000000000..b63a42738 --- /dev/null +++ b/src/components/Icons/Icons.astro @@ -0,0 +1,31 @@ +--- +import { Icons } from "./Icons" +interface Props { + name: keyof typeof Icons + label?: string + color?: string + size?: string + class?: string +} +const { name, label, size = "1em", color } = Astro.props +const a11yAttrs = label ? ({ "aria-label": label } as const) : ({ "aria-hidden": "true" } as const) +--- + + + + diff --git a/src/components/Icons/Icons.ts b/src/components/Icons/Icons.ts new file mode 100644 index 000000000..4aed2474f --- /dev/null +++ b/src/components/Icons/Icons.ts @@ -0,0 +1,155 @@ +const BuiltInIcons = { + "up-caret": + '', + "down-caret": + '', + "right-caret": + '', + "right-arrow": + '', + "left-arrow": + '', + bars: '', + translate: + '', + pencil: + '', + pen: '', + document: + '', + "add-document": + '', + setting: + '', + external: + '', + moon: '', + sun: '', + laptop: + '', + "open-book": + '', + information: + '', + magnifier: + '', + "forward-slash": + '', + close: + '', + error: + '', + warning: + '', + "approve-check-circle": + '', + "approve-check": + '', + rocket: + '', + star: '', + puzzle: + '', + "list-format": + '', + random: + '', + comment: + '', + "comment-alt": + '', + heart: + '', + github: + '', + gitlab: + '', + bitbucket: + '', + codePen: + '', + farcaster: + '', + discord: + '', + gitter: + '', + twitter: + '', + "x.com": + '', + mastodon: + '', + codeberg: + '', + youtube: + '', + threads: + '', + linkedin: + '', + twitch: + '', + microsoftTeams: + '', + instagram: + '', + stackOverflow: + '', + telegram: + '', + rss: '', + facebook: + '', + email: + '', + reddit: + '', + patreon: + '', + signal: + '', + slack: + '', + matrix: + '', + hackerOne: + '', + openCollective: + '', + blueSky: + '', + discourse: + '', + zulip: + '', + astro: + '', + alpine: '', + pnpm: '', + biome: + '', + bun: '', + mdx: '', + apple: + '', + linux: + '', + homebrew: + '', + nix: '', + starlight: + '', + pkl: '', + node: '', + cloudflare: + '', + vercel: '', + netlify: + '', + deno: '', +} + +export const Icons = { + ...BuiltInIcons, +} diff --git a/src/components/LeftSidebar/LeftSidebar.astro b/src/components/LeftSidebar/LeftSidebar.astro index 86e9dda68..a306a36c6 100644 --- a/src/components/LeftSidebar/LeftSidebar.astro +++ b/src/components/LeftSidebar/LeftSidebar.astro @@ -1,8 +1,7 @@ --- import { MENU, Frontmatter, getSidebar } from "../../config" import { localizePath } from "astro-i18next" -import { changeLanguage } from "i18next" -import i18next from "i18next" +import Icon from "../Icons/Icons.astro" export type Props = { currentPage: string @@ -26,6 +25,23 @@ const removeSlashes = function (url: string) { } const currentPageMatch = removeSlashes(currentPage.slice(1)) + +const processedSidebarSections = sidebarSections.map((section) => { + return { + ...section, + contents: section.contents.map((child) => { + const isCurrent = currentPageMatch === removeSlashes(localizePath(child.url)) + return { + ...child, + isCurrent: isCurrent, + open: + isCurrent || + (child.children && + child.children.some((subChild) => currentPageMatch === removeSlashes(localizePath(subChild.url)))), + } + }), + } +}) ---
{!!rating && (
{isSent ? ( -
+
We appreciate your feedback! 🤎
) : ( -
+ -
+