@ethicdevs/gitfoss | Show object: 7f75bc2ca15fa6ede3d368cac422083013049c3c ∙ GitFOSS
feat(drawer): make them mobile first
+ 933
- 324
@@ -1 +0,0 @@
-Branch B\n

@@ -1,5 +1,5 @@
 {
-  "_generatedAtUnix": 1778731850286,
+  "_generatedAtUnix": 1778816347541,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "assets": {

...
@@ -102,7 +102,7 @@
   },
   "views": {
     "HomeView": {
-      "hash": "02d7348a7966d0bab30badacd77703cec1446515",
+      "hash": "9af14ca668298d8cf3ddd309fe29ab8bc93be7b7",
       "pathSource": "./app/views/HomeView.tsx"
     },
     "InternalErrorView": {

...
@@ -170,7 +170,7 @@
       "pathSource": "./app/views/repository/RepositoryForkView.tsx"
     },
     "RepositoryShowObjectView": {
-      "hash": "057fe75366d6705671a24bac56d201581ac54567",
+      "hash": "3c5578449f0a838f9a8b679ec2360a212f5a4e4c",
       "pathSource": "./app/views/repository/RepositoryShowObjectView.tsx"
     },
     "RepositoryPullRequestCreateView": {

app/components/Button.styled.ts
@@ -1,7 +1,8 @@
 // 3rd-party
-import styled, { css } from "styled-components";
+import { css } from "styled-components";
+import styledContainerQuery from "styled-container-query";
 // app
-import { Colors } from "../utils/style";
+import { breakpoints, Colors } from "../utils/style";
 
 type ButtonProps = {
   disabled?: boolean;

...
@@ -50,6 +51,15 @@ const baseButtonCss = css<ButtonProps>`
     padding-top 140ms ease-in-out 0s,
     transform 140ms ease-in-out 0s;
 
+  &:container(max-width: 119px) {
+    font-size: 11px;
+    background: red !important;
+  }
+  &:container(min-width: 120px) {
+    font-size: 18px;
+    background: green !important;
+  }
+
   &:not(:disabled) {
     cursor: pointer;
   }

...
@@ -78,13 +88,17 @@ const baseButtonCss = css<ButtonProps>`
   }
 
   ${({ disabled }) => disabled && disabledButtonCss};
+
+  // @media only screen and (max-width: ${breakpoints.sm}) {
+  //   font-size: 11px;
+  // }
 `;
 
-export const Button = styled.button<ButtonProps>`
+export const Button = styledContainerQuery.button<ButtonProps>`
   ${baseButtonCss};
 `;
 
-export const ButtonAnchor = styled.a<ButtonProps>`
+export const ButtonAnchor = styledContainerQuery.a<ButtonProps>`
   ${baseButtonCss};
   text-decoration: none;
   &:hover {

app/components/DrawerPrimary.tsx
@@ -89,111 +89,101 @@ export const DrawerPrimary = ({
   }
 
   return (
-    <StyledDrawerPrimary id="drawer" themeScheme={themeScheme}>
-      <StyledDrawerHeader>
-        <StyledLogoArea themeScheme={themeScheme}>
-          <a href={"/"}>
-            <h1>{Const.APP_NAME}</h1>
-          </a>
-        </StyledLogoArea>
-      </StyledDrawerHeader>
-      <StyledDrawerContent>
-        <StyledDrawerListHeader style={{ margin: 0 }}>
-          <a href={buildRouteLink(AppRoute.ORGANIZATION_DETAILS, { orgSlug })}>
-            @{orgSlug}
-          </a>
-          <span>{" / "}</span>
-          <a
-            href={buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
-              orgSlug,
-              repoSlug,
-            })}
-          >
-            <TextEllipsis>{repoSlug}</TextEllipsis>
-          </a>
-        </StyledDrawerListHeader>
-        <StyledDrawerListHeader style={{ height: 32 }}>
-          <Grid.Row
-            fluid
-            nowrap
-            gap={8}
-            justifyContent={"center"}
-            alignItems={"center"}
-          >
-            <Grid.Row nowrap gap={4} alignItems={"center"}>
-              <GitPullIcon
-                color={NamedColors.TEXT_MUTED[themeScheme]}
-                size={16}
-              />
-              <span style={{ fontSize: 11 }}>Watchers</span>
-              <Chip
-                themeScheme={themeScheme}
-                color={NamedColors.TEXT_MUTED[themeScheme]}
-                style={{
-                  padding: "2px 6px",
-                  backgroundColor: "rgba(0,0,0,0.1)",
-                  fontSize: 11,
-                  color: NamedColors.TEXT_MUTED[themeScheme],
-                }}
-              >
-                {counters.watchers || "0"}
-              </Chip>
-            </Grid.Row>
-            <Grid.Row nowrap gap={4} alignItems={"center"}>
-              <LikeIcon color={NamedColors.TEXT_MUTED[themeScheme]} size={16} />
-              <span style={{ fontSize: 11 }}>Likes</span>
-              <Chip
-                themeScheme={themeScheme}
-                color={NamedColors.TEXT_MUTED[themeScheme]}
-                style={{
-                  padding: "2px 6px",
-                  backgroundColor: "rgba(0,0,0,0.1)",
-                  fontSize: 11,
-                  color: NamedColors.TEXT_MUTED[themeScheme],
-                }}
-              >
-                {counters.likes || "0"}
-              </Chip>
-            </Grid.Row>
-            <Grid.Row nowrap gap={4} alignItems={"center"}>
-              <GitForkIcon color={Colors.WHITE_01} size={16} />
-              <span style={{ fontSize: 11 }}>Forks</span>
-              <Chip
-                themeScheme={themeScheme}
-                color={NamedColors.TEXT_MUTED[themeScheme]}
-                style={{
-                  padding: "2px 6px",
-                  backgroundColor: "rgba(0,0,0,0.1)",
-                  fontSize: 11,
-                  color: NamedColors.TEXT_MUTED[themeScheme],
-                }}
-              >
-                {counters.forks || "0"}
-              </Chip>
+    <>
+      <StyledDrawerPrimary id="drawer" themeScheme={themeScheme}>
+        <StyledDrawerHeader>
+          <StyledLogoArea themeScheme={themeScheme}>
+            <a href={"/"}>
+              <h1>{Const.APP_NAME}</h1>
+            </a>
+          </StyledLogoArea>
+        </StyledDrawerHeader>
+        <StyledDrawerContent>
+          <StyledDrawerListHeader style={{ margin: 0 }}>
+            <a
+              href={buildRouteLink(AppRoute.ORGANIZATION_DETAILS, { orgSlug })}
+            >
+              @{orgSlug}
+            </a>
+            <span>{" / "}</span>
+            <a
+              href={buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
+                orgSlug,
+                repoSlug,
+              })}
+            >
+              <TextEllipsis>{repoSlug}</TextEllipsis>
+            </a>
+          </StyledDrawerListHeader>
+          <StyledDrawerListHeader style={{ height: 32 }}>
+            <Grid.Row
+              fluid
+              nowrap
+              gap={8}
+              justifyContent={"center"}
+              alignItems={"center"}
+            >
+              <Grid.Row nowrap gap={4} alignItems={"center"}>
+                <GitPullIcon
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  size={16}
+                />
+                <span style={{ fontSize: 11 }}>Watchers</span>
+                <Chip
+                  themeScheme={themeScheme}
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  style={{
+                    padding: "2px 6px",
+                    backgroundColor: "rgba(0,0,0,0.1)",
+                    fontSize: 11,
+                    color: NamedColors.TEXT_MUTED[themeScheme],
+                  }}
+                >
+                  {counters.watchers || "0"}
+                </Chip>
+              </Grid.Row>
+              <Grid.Row nowrap gap={4} alignItems={"center"}>
+                <LikeIcon
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  size={16}
+                />
+                <span style={{ fontSize: 11 }}>Likes</span>
+                <Chip
+                  themeScheme={themeScheme}
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  style={{
+                    padding: "2px 6px",
+                    backgroundColor: "rgba(0,0,0,0.1)",
+                    fontSize: 11,
+                    color: NamedColors.TEXT_MUTED[themeScheme],
+                  }}
+                >
+                  {counters.likes || "0"}
+                </Chip>
+              </Grid.Row>
+              <Grid.Row nowrap gap={4} alignItems={"center"}>
+                <GitForkIcon color={Colors.WHITE_01} size={16} />
+                <span style={{ fontSize: 11 }}>Forks</span>
+                <Chip
+                  themeScheme={themeScheme}
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  style={{
+                    padding: "2px 6px",
+                    backgroundColor: "rgba(0,0,0,0.1)",
+                    fontSize: 11,
+                    color: NamedColors.TEXT_MUTED[themeScheme],
+                  }}
+                >
+                  {counters.forks || "0"}
+                </Chip>
+              </Grid.Row>
             </Grid.Row>
-          </Grid.Row>
-        </StyledDrawerListHeader>
-        <StyledDrawerList>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathFiles}
-            className={
-              ((!commonProps.path!.startsWith(pathPipelines) === false &&
-                commonProps.path!.startsWith(pathFiles)) ||
-                [pathFiles, pathRepo, pathRepoTrailing].some(
-                  (p) =>
-                    commonProps.path === p || commonProps.path!.startsWith(p),
-                )) &&
-              commonProps.path!.startsWith(pathPulls) === false &&
-              commonProps.path!.startsWith(pathPipelines) === false
-                ? "active"
-                : undefined
-            }
-            style={{ paddingRight: 16 }}
-          >
-            <span>Files</span>
-            <FolderIcon
-              color={
+          </StyledDrawerListHeader>
+          <StyledDrawerList>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathFiles}
+              className={
                 ((!commonProps.path!.startsWith(pathPipelines) === false &&
                   commonProps.path!.startsWith(pathFiles)) ||
                   [pathFiles, pathRepo, pathRepoTrailing].some(

...
@@ -202,87 +192,128 @@ export const DrawerPrimary = ({
                   )) &&
                 commonProps.path!.startsWith(pathPulls) === false &&
                 commonProps.path!.startsWith(pathPipelines) === false
-                  ? NamedColors.TEXT_DEFAULT[themeScheme]
-                  : NamedColors.TEXT_MUTED[themeScheme]
+                  ? "active"
+                  : undefined
+              }
+              style={{ paddingRight: 16 }}
+            >
+              <span>Files</span>
+              <FolderIcon
+                color={
+                  ((!commonProps.path!.startsWith(pathPipelines) === false &&
+                    commonProps.path!.startsWith(pathFiles)) ||
+                    [pathFiles, pathRepo, pathRepoTrailing].some(
+                      (p) =>
+                        commonProps.path === p ||
+                        commonProps.path!.startsWith(p),
+                    )) &&
+                  commonProps.path!.startsWith(pathPulls) === false &&
+                  commonProps.path!.startsWith(pathPipelines) === false
+                    ? NamedColors.TEXT_DEFAULT[themeScheme]
+                    : NamedColors.TEXT_MUTED[themeScheme]
+                }
+                size={16}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathPulls}
+              className={
+                commonProps.path! === pathPulls ||
+                commonProps.path!.startsWith(pathPulls)
+                  ? "active"
+                  : undefined
               }
-              size={16}
-            />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathPulls}
-            className={
-              commonProps.path! === pathPulls ||
-              commonProps.path!.startsWith(pathPulls)
-                ? "active"
-                : undefined
-            }
-          >
-            <span>Pull Requests</span>
-            <Chip themeScheme={themeScheme}>{counters.pulls || 0}</Chip>
-          </StyledDrawerListItem>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathPipelines}
-            className={
-              commonProps.path! === pathPipelines ||
-              commonProps.path!.startsWith(pathPipelines)
-                ? "active"
-                : undefined
-            }
-          >
-            <span>Pipelines</span>
-            <Chip themeScheme={themeScheme}>{counters.builds || 0}</Chip>
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Tests & Coverage</span>
-            <Chip themeScheme={themeScheme}>{counters.tests || 0}</Chip>
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Issues</span>
-            <Chip themeScheme={themeScheme}>{counters.issues || 0}</Chip>
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>API Reference</span>
-            <Chip themeScheme={themeScheme}>{counters.apiRefSymbols || 0}</Chip>
-          </StyledDrawerListItem>
-        </StyledDrawerList>
-        <StyledDrawerListHeader></StyledDrawerListHeader>
-        <StyledDrawerList></StyledDrawerList>
-      </StyledDrawerContent>
-      <StyledDrawerFooter>
-        <StyledDrawerList>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Feedback</span>
-            <CommentIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Help Center</span>
-            <span style={{ flex: 1 }}></span>
-            {counters.helpCenterNotifs! > 0 && (
-              <Chip themeScheme={themeScheme}>{counters.helpCenterNotifs}</Chip>
-            )}
-            <SupportIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Settings</span>
-            <SettingsIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-        </StyledDrawerList>
-      </StyledDrawerFooter>
-    </StyledDrawerPrimary>
+            >
+              <span>Pull Requests</span>
+              <Chip themeScheme={themeScheme}>{counters.pulls || 0}</Chip>
+            </StyledDrawerListItem>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathPipelines}
+              className={
+                commonProps.path! === pathPipelines ||
+                commonProps.path!.startsWith(pathPipelines)
+                  ? "active"
+                  : undefined
+              }
+            >
+              <span>Pipelines</span>
+              <Chip themeScheme={themeScheme}>{counters.builds || 0}</Chip>
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Tests & Coverage</span>
+              <Chip themeScheme={themeScheme}>{counters.tests || 0}</Chip>
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Issues</span>
+              <Chip themeScheme={themeScheme}>{counters.issues || 0}</Chip>
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>API Reference</span>
+              <Chip themeScheme={themeScheme}>
+                {counters.apiRefSymbols || 0}
+              </Chip>
+            </StyledDrawerListItem>
+          </StyledDrawerList>
+          <StyledDrawerListHeader></StyledDrawerListHeader>
+          <StyledDrawerList></StyledDrawerList>
+        </StyledDrawerContent>
+        <StyledDrawerFooter>
+          <StyledDrawerList>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Feedback</span>
+              <CommentIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Help Center</span>
+              <span style={{ flex: 1 }}></span>
+              {counters.helpCenterNotifs! > 0 && (
+                <Chip themeScheme={themeScheme}>
+                  {counters.helpCenterNotifs}
+                </Chip>
+              )}
+              <SupportIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Settings</span>
+              <SettingsIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+          </StyledDrawerList>
+        </StyledDrawerFooter>
+      </StyledDrawerPrimary>
+      <StyledDrawerOverlay href="#?"></StyledDrawerOverlay>
+    </>
   );
 };
 
+const StyledDrawerOverlay = styled.a`
+  display: none;
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: 21000;
+  pointer-events: none;
+  user-select: none;
+  background: rgba(0, 0, 0, 0.4);
+  filter: blur(8px);
+  opacity: 0;
+  transition: opacity 140ms ease-in-out 140ms;
+`;
+
 const StyledDrawerPrimary = styled.aside<
   WithThemeSchemeProp & { color?: string }
 >`

...
@@ -304,21 +335,30 @@ const StyledDrawerPrimary = styled.aside<
     background: ${NamedColors.HEADER[themeScheme]};
     border-right: 1px solid ${NamedColors.BORDER_DEFAULT[themeScheme]};
 
-    @media only screen and (max-width: 386px) {
-      display: none;
-
-      position: absolute;
-      top: 0;
-      left: 0;
-      bottom: 0;
-      z-index: 1000;
-      width: 85vw;
-      max-width: 320px;
-      transform: translateX(-100%);
+    transition: transform 140ms cubic-bezier(0, 0, 0.2, 1);
+
+    @media only screen and (max-width: 768px) {
+      & {
+        position: absolute;
+        top: 0;
+        left: 0;
+        bottom: 0;
+        z-index: 10000;
+        width: 85vw;
+        max-width: 320px;
+        transition: transform 140ms cubic-bezier(0, 0, 0.2, 1);
+        transform: translateX(-100%);
+      }
 
       &:target {
         transform: translateX(0);
-        transition: transform 0.3s ease-in-out;
+      }
+
+      &:target ~ ${StyledDrawerOverlay} {
+        display: flex;
+        pointer-events: auto;
+        opacity: 1;
+        z-index: 8000;
       }
     }
   `};

app/components/DrawerSettings.tsx
@@ -61,103 +61,132 @@ export const DrawerSettings = ({
   console.log("counters:", counters);
 
   return (
-    <StyledDrawerSettings id="drawer" themeScheme={themeScheme}>
-      <StyledDrawerHeader>
-        <StyledLogoArea themeScheme={themeScheme}>
-          <a href={"/"}>
-            <h1>{Const.APP_NAME}</h1>
-          </a>
-        </StyledLogoArea>
-      </StyledDrawerHeader>
-      <StyledDrawerContent>
-        <StyledDrawerListHeader>
-          <img
-            src={commonProps.currentUserAvatarUri || ""}
-            style={{
-              width: 20,
-              height: 20,
-              background: "red",
-              borderRadius: 16,
-              marginRight: 8,
-            }}
-          />
-          <a
-            href={buildRouteLink(
-              AppRoute.USER_DETAILS,
-              { username },
-              { encodeURIComponent: false },
-            )}
-          >
-            @{username}
-          </a>
-        </StyledDrawerListHeader>
-        <StyledDrawerList>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathMyAccount}
-            className={
-              // commonProps.path!.startsWith(pathMyAccount) ||
-              commonProps.path! === pathMyAccount ? "active" : undefined
-            }
-          >
-            <span>My Account</span>
-            <UserIcon color={NamedColors.TEXT_DEFAULT[themeScheme]} size={20} />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathSSHKeys}
-            className={
-              commonProps.path! === pathSSHKeys ||
-              commonProps.path!.startsWith(pathSSHKeys)
-                ? "active"
-                : undefined
-            }
-          >
-            <span>SSH Keys</span>
-            <Chip themeScheme={themeScheme} style={{ marginRight: 8 }}>
-              {counters.sshKeys || 0}
-            </Chip>
-            <KeyIcon color={NamedColors.TEXT_DEFAULT[themeScheme]} size={24} />
-          </StyledDrawerListItem>
-        </StyledDrawerList>
-        <StyledDrawerListHeader></StyledDrawerListHeader>
-        <StyledDrawerList></StyledDrawerList>
-      </StyledDrawerContent>
-      <StyledDrawerFooter>
-        <StyledDrawerList>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Feedback</span>
-            <CommentIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
+    <>
+      <StyledDrawerSettings id="drawer" themeScheme={themeScheme}>
+        <StyledDrawerHeader>
+          <StyledLogoArea themeScheme={themeScheme}>
+            <a href={"/"}>
+              <h1>{Const.APP_NAME}</h1>
+            </a>
+          </StyledLogoArea>
+        </StyledDrawerHeader>
+        <StyledDrawerContent>
+          <StyledDrawerListHeader>
+            <img
+              src={commonProps.currentUserAvatarUri || ""}
+              style={{
+                width: 20,
+                height: 20,
+                background: "red",
+                borderRadius: 16,
+                marginRight: 8,
+              }}
             />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Help Center</span>
-            {counters.helpCenterNotifs! > 0 && (
-              <Chip themeScheme={themeScheme}>{counters.helpCenterNotifs}</Chip>
-            )}
-            <SupportIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={buildRouteLink(AppRoute.AUTH_LOGOUT_ACTION, null)}
-          >
-            <span>Logout</span>
-            <LogOutIcon
-              color={NamedColors.TEXT_DEFAULT[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-        </StyledDrawerList>
-      </StyledDrawerFooter>
-    </StyledDrawerSettings>
+            <a
+              href={buildRouteLink(
+                AppRoute.USER_DETAILS,
+                { username },
+                { encodeURIComponent: false },
+              )}
+            >
+              @{username}
+            </a>
+          </StyledDrawerListHeader>
+          <StyledDrawerList>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathMyAccount}
+              className={
+                // commonProps.path!.startsWith(pathMyAccount) ||
+                commonProps.path! === pathMyAccount ? "active" : undefined
+              }
+            >
+              <span>My Account</span>
+              <UserIcon
+                color={NamedColors.TEXT_DEFAULT[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathSSHKeys}
+              className={
+                commonProps.path! === pathSSHKeys ||
+                commonProps.path!.startsWith(pathSSHKeys)
+                  ? "active"
+                  : undefined
+              }
+            >
+              <span>SSH Keys</span>
+              <Chip themeScheme={themeScheme} style={{ marginRight: 8 }}>
+                {counters.sshKeys || 0}
+              </Chip>
+              <KeyIcon
+                color={NamedColors.TEXT_DEFAULT[themeScheme]}
+                size={24}
+              />
+            </StyledDrawerListItem>
+          </StyledDrawerList>
+          <StyledDrawerListHeader></StyledDrawerListHeader>
+          <StyledDrawerList></StyledDrawerList>
+        </StyledDrawerContent>
+        <StyledDrawerFooter>
+          <StyledDrawerList>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Feedback</span>
+              <CommentIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Help Center</span>
+              {counters.helpCenterNotifs! > 0 && (
+                <Chip themeScheme={themeScheme}>
+                  {counters.helpCenterNotifs}
+                </Chip>
+              )}
+              <SupportIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={buildRouteLink(AppRoute.AUTH_LOGOUT_ACTION, null)}
+            >
+              <span>Logout</span>
+              <LogOutIcon
+                color={NamedColors.TEXT_DEFAULT[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+          </StyledDrawerList>
+        </StyledDrawerFooter>
+      </StyledDrawerSettings>
+      <StyledDrawerOverlay href="#?"></StyledDrawerOverlay>
+    </>
   );
 };
 
+const StyledDrawerOverlay = styled.a`
+  display: none;
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: 21000;
+  pointer-events: none;
+  user-select: none;
+  background: rgba(0, 0, 0, 0.4);
+  filter: blur(8px);
+  opacity: 0;
+  transition: opacity 140ms ease-in-out 140ms;
+`;
+
 const StyledDrawerSettings = styled.aside<
   WithThemeSchemeProp & { color?: string }
 >`

...
@@ -179,21 +208,30 @@ const StyledDrawerSettings = styled.aside<
     background: ${NamedColors.HEADER[themeScheme]};
     border-right: 1px solid ${NamedColors.BORDER_DEFAULT[themeScheme]};
 
-    @media only screen and (max-width: 386px) {
-      display: none;
-
-      position: absolute;
-      top: 0;
-      left: 0;
-      bottom: 0;
-      z-index: 1000;
-      width: 85vw;
-      max-width: 320px;
-      transform: translateX(-100%);
+    transition: transform 140ms cubic-bezier(0, 0, 0.2, 1);
+
+    @media only screen and (max-width: 768px) {
+      & {
+        position: absolute;
+        top: 0;
+        left: 0;
+        bottom: 0;
+        z-index: 10000;
+        width: 85vw;
+        max-width: 320px;
+        transition: transform 140ms cubic-bezier(0, 0, 0.2, 1);
+        transform: translateX(-100%);
+      }
 
-      &.target {
+      &:target {
         transform: translateX(0);
-        transition: transform 0.3s ease-in-out;
+      }
+
+      &:target ~ ${StyledDrawerOverlay} {
+        display: flex;
+        pointer-events: auto;
+        opacity: 1;
+        z-index: 8000;
       }
     }
   `};

app/components/Layout.tsx
@@ -122,6 +122,7 @@ const LayoutComponent: FC<LayoutProps & WithThemeSchemeProp> = (props) => {
               <PageHeader
                 commonProps={props as any}
                 themeScheme={themeScheme}
+                showBurgerMenu={showDrawerPrimary || showDrawerSettings}
                 forceShowLogo={
                   showDrawerPrimary !== true && showDrawerSettings !== true
                 }

app/components/PageHeader.tsx
@@ -16,6 +16,7 @@ import { PlusIcon } from "./icons/PlusIcon";
 
 interface PageHeaderProps extends CommonProps {
   forceShowLogo?: boolean;
+  showBurgerMenu?: boolean;
   setDrawerPrimaryOpen?: (predicate: (prev: boolean) => boolean) => void;
   setDrawerSettingsOpen?: (predicate: (prev: boolean) => boolean) => void;
 }

...
@@ -24,6 +25,7 @@ export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({
   commonProps,
   themeScheme,
   forceShowLogo = true,
+  showBurgerMenu = false,
   // setDrawerPrimaryOpen = undefined,
   // setDrawerSettingsOpen = undefined,
 }) => {

...
@@ -84,16 +86,18 @@ export const PageHeader: VFC<PageHeaderProps & WithThemeSchemeProp> = ({
   return (
     <StyledPageHeader themeScheme={themeScheme}>
       <PageWrapper style={{ gap: 12 }}>
-        <StyledBurgerMenu
-          themeScheme={themeScheme}
-          // onClick={toggleDrawerPrimary}
-          href="#drawer"
-        >
-          <BurgerMenuIcon
-            color={NamedColors.TEXT_DEFAULT[themeScheme]}
-            size={24}
-          />
-        </StyledBurgerMenu>
+        {showBurgerMenu && (
+          <StyledBurgerMenu
+            themeScheme={themeScheme}
+            // onClick={toggleDrawerPrimary}
+            href="#drawer"
+          >
+            <BurgerMenuIcon
+              color={NamedColors.TEXT_DEFAULT[themeScheme]}
+              size={24}
+            />
+          </StyledBurgerMenu>
+        )}
         <StyledLogoArea themeScheme={themeScheme} forceShowLogo={forceShowLogo}>
           <a href={"/"}>
             <h1>{Const.APP_NAME}</h1>

app/views/HomeView.tsx
@@ -231,7 +231,7 @@ const HomeView: ReactView<HomeViewProps> = (props) => {
                 .feature-section {
                   display: flex;
                   width: 100%;
-                  min-height: 80vh;
+                  min-height: 90vh;
                   flex-direction: row;
                   align-items: center;
                   gap: 48px;

@@ -63,7 +63,8 @@
     "react-dom": "^17.0.2",
     "react-is": "^17.0.2",
     "styled-components": "^5.3.5",
-    "styled-container-queries": "^1.0.0"
+    "styled-container-queries": "^1.0.0",
+    "styled-container-query": "^1.3.5"
   },
   "devDependencies": {
     "@babel/core": "^7.0.0-0",

packages/gitfoss-ci-runner/example.ci.yml
@@ -1,6 +1,6 @@
 stages:
-  - name: hello
-    image: alpine:3.18
+  - name: hello-android
+    image: webcuisine/gitlab-ci-react-native-android:latest
     workdir: ./.workdir/:/home/node/
     env:
       FOO: "$HOME"

packages/gitfoss-ci-runner/src/command.cr
@@ -14,8 +14,8 @@ struct Command
   cached : Array(String)
   shell : String
   workdir : String
-  artifacts_to_set : Hash(String, String)   # was Stash
-  artifacts_to_get : Hash(String, String)   # was UnStash
+  artifacts_to_set : Hash(String, String)   # was stash
+  artifacts_to_get : Hash(String, String)   # was unstash
 
   def initialize(
     @image : String,

new file
packages/gitfoss-ci-runner/src/event_bus.cr
@@ -0,0 +1,302 @@
+# Event bus with graceful shutdown, wildcard topic matching
+# serialization helpers, and error handling
+require "json"
+require "mutex"
+
+# Payload type alias
+alias Payload = JSON::Any
+
+struct Event
+  getter topic : String
+  getter payload : Payload
+  getter timestamp : Time = Time.local
+
+  def initialize(topic : String, payload : Payload)
+    @topic = topic
+    @payload = payload
+    @timestamp = Time.local
+  end
+
+  # Serialization helper -> JSON string
+  def to_json : String
+    JSON.build do |j|
+      j.object do
+        j.field "topic", topic
+        j.field "payload", payload_to_json(payload)
+        j.field "timestamp", timestamp.to_unix.to_i
+      end
+    end
+  end
+
+  # Parse from JSON string (expects same shape)
+  def self.from_json(json_str : String) : Event
+    parsed = JSON.parse(json_str).as_h
+    topic = parsed["topic"].as_s
+    payload = json_to_payload(parsed["payload"])
+    Event.new(topic, payload)
+  end
+
+  private def payload_to_json(p)
+    case p
+    when String, Int32, Float32, Bool, Nil
+      p
+    when JSON::Any
+      p
+    when Array
+      p.as_a.map(&.payload_to_json)
+    when Hash
+      h = {} of String => JSON::Any
+      p.as_h.each { |k, v| h[k] = payload_to_json(v) }
+      h
+    else
+      raise "Unsupported payload type: #{p.class}"
+    end
+  end
+
+  private def self.json_to_payload(j) : Payload
+    j
+    # JSON::Any -> convert to allowed Payload structures
+    # case j
+    # when JSON::Any
+    #   case j
+    #   when j.is_a?(String) then j.as_s
+    #   when j.is_a?(Int) then j.as_i
+    #   when j.is_a?(Float) then j.as_f32
+    #   when j.is_a?(Bool) then j.as_bool
+    #   when j.is_a?(Nil) then nil
+    #   when j.is_a?(Array)
+    #     j.as_a.map { |el| json_to_payload(el).as(JSON::Any) }
+    #   when j.is_a?(Hash)
+    #     res = {} of String => Payload
+    #     j.as_h.each { |k, v| res[k] = json_to_payload(v).as(JSON::Any) }
+    #     res
+    #   else
+    #     raise "Unsupported JSON::Any variant"
+    #   end
+    # else
+    #   # primitive Crystal types (if already converted)
+    #   j
+    # end
+  end
+end
+
+# Simple wildcard matcher:
+# - "*" matches any single topic exactly "*"
+# Pattern matchers:
+# - "#" as multi-level wildcard (matches any suffix, e.g., "user.#" matches "user.login", "user.signup.confirm")
+# - "+" as single-level wildcard (matches exactly one token between dots, e.g., "user.+.created")
+module TopicMatcher
+  def self.matches?(pattern : String, topic : String) : Bool
+    return true if pattern == "*"
+    pat_tokens = pattern.split(".")
+    t_tokens = topic.split(".")
+
+    i = 0
+    j = 0
+    while i < pat_tokens.size && j < t_tokens.size
+      pt = pat_tokens[i]
+      if pt == "#"
+        return true
+      elsif pt == "+"
+        i += 1
+        j += 1
+        next
+      elsif pt == t_tokens[j]
+        i += 1
+        j += 1
+        next
+      else
+        return false
+      end
+    end
+
+    # consume remaining pattern tokens: if only trailing "#" remain, match
+    while i < pat_tokens.size
+      return true if pat_tokens[i] == "#"
+      break
+    end
+
+    i == pat_tokens.size && j == t_tokens.size
+  end
+end
+
+class Subscriber
+  getter patterns : Array(String)
+  getter ch : Channel(Event)
+  getter id : Int32
+
+  def initialize(id : Int32, patterns : Array(String), buffer = 10)
+    @id = id
+    @patterns = patterns
+    @ch = Channel(Event).new(buffer)
+  end
+
+  def matches?(topic : String) : Bool
+    @patterns.any? { |pat| TopicMatcher.matches?(pat, topic) }
+  end
+end
+
+class EventBus
+  @dispatcher_fiber : Fiber
+
+  def initialize
+    @inbox = Channel(Event).new
+    @subs = [] of Subscriber
+    @mutex = Mutex.new
+    @closing = false
+    @next_sub_id = 1_i32
+    @dispatcher_fiber = spawn run
+  end
+
+  def publish(event : Event)
+    raise "EventBus is shutting down, cannot publish" if @closing
+    @inbox.send(event)
+  end
+
+  # subscribe accepts a pattern string (or array of patterns) using + and # like MQTT
+  # block handler runs in a spawned fiber; errors from handler are caught and forwarded to error_handler if set
+  def subscribe(patterns : String | Array(String), buffer = 10, &handler : Proc(Event, Nil))
+    pattern_list = patterns.is_a?(String) ? [patterns] : patterns
+    @mutex.synchronize do
+      raise "EventBus is shutting down" if @closing
+    end
+    id = @next_sub_id
+    @next_sub_id += 1
+    sub = Subscriber.new(id, pattern_list, buffer)
+    @mutex.synchronize { @subs << sub }
+
+    spawn do
+      begin
+        loop do
+          e = sub.ch.receive
+          begin
+            handler.call(e)
+          rescue ex
+            # send to configured error handler if available, else print
+            if @error_handler
+              begin
+                @error_handler.not_nil!.call(sub, e, ex)
+              rescue err2
+                STDERR.puts "Subscriber error handler failed: #{err2.message}"
+              end
+            else
+              STDERR.puts "Subscriber ##{sub.id} handler error: #{ex.message}"
+            end
+          end
+        end
+      rescue Channel::ClosedError
+        # exit
+      end
+    end
+
+    sub
+  end
+
+  # Allows setting a centralized error handler: Proc(Subscriber, Event, Exception)
+  def set_error_handler(&block : Proc(Subscriber, Event, Exception, Nil))
+    @error_handler = block
+  end
+
+  def unsubscribe(sub : Subscriber)
+    @mutex.synchronize do
+      @subs.delete(sub)
+    end
+    sub.ch.close
+  end
+
+  # Graceful shutdown:
+  # - stop accepting new publishes
+  # - close inbox so dispatcher drains
+  # - wait for dispatcher to finish dispatching
+  # - close all subscriber channels (so subscriber handler fibers exit)
+  def shutdown(timeout_seconds = 5_f32)
+    @mutex.synchronize do
+      return if @closing
+      @closing = true
+    end
+
+    begin
+      @inbox.close
+    rescue
+    end
+
+    # wait for dispatcher fiber to finish (simple busy-wait with timeout)
+    started = Time.local
+    while @dispatcher_fiber.dead? == false
+      break if (Time.local - started).seconds > timeout_seconds
+      sleep(0.01.seconds)
+    end
+
+    # close subscribers
+    @mutex.synchronize do
+      @subs.each { |s| s.ch.close rescue nil }
+      @subs.clear
+    end
+  end
+
+  private def run
+    loop do
+      e = @inbox.receive
+      @mutex.synchronize do
+        # dispatch to matching subscribers
+        @subs.each do |sub|
+          if sub.matches?(e.topic)
+            # non-blocking dispatch: send in spawned fiber, ignore if closed
+            spawn { sub.ch.send(e) rescue nil }
+          end
+        end
+      end
+    end
+  rescue Channel::ClosedError
+    # inbox closed -> exit dispatcher
+  end
+end
+
+# ---------------- Example usage ----------------
+bus = EventBus.new
+
+# Set centralized error handler
+bus.set_error_handler do |sub, event, ex|
+  STDERR.puts "Error in subscriber ##{sub.id} handling #{event.topic}: #{ex.class} - #{ex.message}"
+end
+
+# Subscribe with MQTT-style patterns
+all = bus.subscribe("*") do |e|
+  puts "[*] #{e.topic}: #{e.payload.inspect}"
+end
+user_any = bus.subscribe("user.#") do |e|
+  puts "[user.#] #{e.topic}: #{e.payload.inspect}"
+end
+login_specific = bus.subscribe("user.login") do |e|
+  puts "[user.login] #{e.payload.inspect}"
+end
+single_level = bus.subscribe("order.+.created") do |e|
+  puts "[order.+.created] got #{e.topic}"
+end
+
+# Publish a variety of events (using typed payloads)
+bus.publish Event.new("user.signup", JSON.parse(%({"email": "alice@example.com"})))
+bus.publish Event.new("user.signup.confirm", JSON.parse(%({"email": "alice@example.com"})))
+bus.publish Event.new("user.login", JSON.parse(%({"id": 123, "method": "oauth"})))
+bus.publish Event.new("order.us.created", JSON.parse(%({"order_id": "o-1", "total": 42})))
+bus.publish Event.new("order.eu.created", JSON.parse(%({"order_id": "o-2", "total": 55})))
+
+# Demonstrate serialization helpers
+# e = Event.new("meta", JSON.parse(%({"k":"v","n":1})))
+# json_str = e.to_json
+# puts "Serialized event: #{json_str}"
+# parsed = Event.from_json(json_str)
+# puts "Parsed event topic: #{parsed.topic}, payload: #{parsed.payload.inspect}"
+
+# Trigger an error in a handler to show central error handling
+bus.subscribe("bad.handler") do |ev|
+  raise "boom for #{ev.topic}"
+end
+
+bus.publish Event.new("bad.handler", JSON.parse(%(null)))
+
+sleep(0.1.seconds)
+
+# Graceful shutdown
+bus.shutdown(2.0)
+puts "Shutdown complete."

@@ -1,5 +1,6 @@
 todo:
 
+- [ ] add pipelines feature (wip: ci-runner: done, ui: wip api: wip)
 - [x] make http git push work
 - [x] make ssh git push work
 - [ ] make ssh git push work every times

new file
types/styled-container-query.d.ts
@@ -0,0 +1,184 @@
+declare module "styled-container-query" {
+  import * as React from "react";
+  import styledComponents from "styled-components";
+
+  export type ContainerQueryRule = {
+    minWidth?: string;
+    maxWidth?: string;
+    minHeight?: string;
+    maxHeight?: string;
+  };
+
+  export type ContainerQueryRules = {
+    [className: string]: ContainerQueryRule;
+  };
+
+  export function matchQueries(
+    rules: ContainerQueryRules,
+    size: { width?: number | null; height?: number | null },
+  ): string[];
+
+  export function withQueryContainer<P = any>(
+    StyledComponent: React.ComponentType<P>,
+    rules: ContainerQueryRules,
+  ): React.ForwardRefExoticComponent<
+    React.PropsWithoutRef<
+      P & { children?: React.ReactNode; query?: ContainerQueryRules }
+    > &
+      React.RefAttributes<any>
+  >;
+
+  export function unitToPx(
+    element: Element | null,
+    value: string,
+    prop: "width" | "height" | string,
+  ): number;
+
+  export interface StyledContainerQuery {
+    <Props = any>(
+      component: React.ComponentType<Props> | string,
+    ): styledComponents.ThemedStyledFunction<any, any, {}, never>;
+
+    a: any;
+    abbr: any;
+    address: any;
+    area: any;
+    article: any;
+    aside: any;
+    audio: any;
+    b: any;
+    base: any;
+    bdi: any;
+    bdo: any;
+    big: any;
+    blockquote: any;
+    body: any;
+    br: any;
+    button: any;
+    canvas: any;
+    caption: any;
+    cite: any;
+    code: any;
+    col: any;
+    colgroup: any;
+    data: any;
+    datalist: any;
+    dd: any;
+    del: any;
+    details: any;
+    dfn: any;
+    dialog: any;
+    div: any;
+    dl: any;
+    dt: any;
+    em: any;
+    embed: any;
+    fieldset: any;
+    figcaption: any;
+    figure: any;
+    footer: any;
+    form: any;
+    h1: any;
+    h2: any;
+    h3: any;
+    h4: any;
+    h5: any;
+    h6: any;
+    head: any;
+    header: any;
+    hgroup: any;
+    hr: any;
+    html: any;
+    i: any;
+    iframe: any;
+    img: any;
+    input: any;
+    ins: any;
+    kbd: any;
+    keygen: any;
+    label: any;
+    legend: any;
+    li: any;
+    link: any;
+    main: any;
+    map: any;
+    mark: any;
+    marquee: any;
+    menu: any;
+    menuitem: any;
+    meta: any;
+    meter: any;
+    nav: any;
+    noscript: any;
+    object: any;
+    ol: any;
+    optgroup: any;
+    option: any;
+    output: any;
+    p: any;
+    param: any;
+    picture: any;
+    pre: any;
+    progress: any;
+    q: any;
+    rp: any;
+    rt: any;
+    ruby: any;
+    s: any;
+    samp: any;
+    script: any;
+    section: any;
+    select: any;
+    small: any;
+    source: any;
+    span: any;
+    strong: any;
+    style: any;
+    sub: any;
+    summary: any;
+    sup: any;
+    table: any;
+    tbody: any;
+    td: any;
+    textarea: any;
+    tfoot: any;
+    th: any;
+    thead: any;
+    time: any;
+    title: any;
+    tr: any;
+    track: any;
+    u: any;
+    ul: any;
+    var: any;
+    video: any;
+    wbr: any;
+    circle: any;
+    clipPath: any;
+    defs: any;
+    ellipse: any;
+    foreignObject: any;
+    g: any;
+    image: any;
+    line: any;
+    linearGradient: any;
+    marker: any;
+    mask: any;
+    path: any;
+    pattern: any;
+    polygon: any;
+    polyline: any;
+    radialGradient: any;
+    rect: any;
+    stop: any;
+    svg: any;
+    text: any;
+    tspan: any;
+
+    [element: string]: any;
+  }
+
+  const styledContainerQuery: StyledContainerQuery;
+  export { styledContainerQuery as styledCQ };
+  export default styledContainerQuery;
+}

@@ -1537,6 +1537,11 @@ classifier@~0.1.0:
     redis ">=0.7.0"
     underscore ">=1.1.0"
 
+classnames@^2.2.6:
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
+  integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
+
 cliui@^7.0.2:
   version "7.0.4"
   resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"

...
@@ -2643,7 +2648,7 @@ himalaya@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/himalaya/-/himalaya-1.1.0.tgz#31724ae9d35714cd7c6f4be94888953f3604606a"
 
-hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0:
+hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
   dependencies:

...
@@ -4440,6 +4445,11 @@ require-from-string@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
 
+resize-observer@^1.0.0:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/resize-observer/-/resize-observer-1.0.4.tgz#48beb64602ce408ebd1a433784d64ef76f38d321"
+  integrity sha512-AQ2MdkWTng9d6JtjHvljiQR949qdae91pjSNugGGeOFzKIuLHvoZIYhUTjePla5hCFDwQHrnkciAIzjzdsTZew==
+
 resolve-cwd@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"

...
@@ -4630,6 +4640,11 @@ shell-quote@^1.6.1:
   version "1.7.3"
   resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123"
 
+shorthash@0.0.2:
+  version "0.0.2"
+  resolved "https://registry.yarnpkg.com/shorthash/-/shorthash-0.0.2.tgz#59b268eecbde59038b30da202bcfbddeb2c4a4eb"
+  integrity sha512-L/QRElsfTaCCzA7AJPXuin6/jgWjgmTfjdaXucQ5PauPypmqAZ7t4GueaCv+Jti0M8S2Iv1C/ryD+aWY/KUGCA==
+
 side-channel@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"

...
@@ -4846,6 +4861,16 @@ styled-container-queries@^1.0.0:
   resolved "https://registry.yarnpkg.com/styled-container-queries/-/styled-container-queries-1.0.0.tgz#b1a0ec16d1d89d6bc4fd8eead57171d4f1afe530"
   integrity sha512-Rxvb0brymST4qqf0ckW+kAJuuyPuXOr4uRvNnJ5tbqNwOoSpmvpWj5KcJlV9mNs2xq8hozRD1s9lx3+Azwl4Dg==
 
+styled-container-query@^1.3.5:
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/styled-container-query/-/styled-container-query-1.3.5.tgz#15ab590d32e20f879bcb9e8d882da65da7ab22a9"
+  integrity sha512-YzL4+CC+OZqkS94xZkxz2MsP6JjsJi2VKYC4JnhzWinWAJ0Mnj8Gmuw71mixg40D4BOOrXXXmt8AekyXSo10cg==
+  dependencies:
+    classnames "^2.2.6"
+    hoist-non-react-statics "^3.3.2"
+    resize-observer "^1.0.0"
+    shorthash "0.0.2"
+
 supports-color@^5.3.0, supports-color@^5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"

feat(drawer): make them mobile first
+ 933
- 324
@@ -1 +0,0 @@
-Branch B\n

@@ -1,5 +1,5 @@
 {
-  "_generatedAtUnix": 1778731850286,
+  "_generatedAtUnix": 1778816347541,
   "_hashAlgorithm": "sha1",
   "_version": 2,
   "assets": {

...
@@ -102,7 +102,7 @@
   },
   "views": {
     "HomeView": {
-      "hash": "02d7348a7966d0bab30badacd77703cec1446515",
+      "hash": "9af14ca668298d8cf3ddd309fe29ab8bc93be7b7",
       "pathSource": "./app/views/HomeView.tsx"
     },
     "InternalErrorView": {

...
@@ -170,7 +170,7 @@
       "pathSource": "./app/views/repository/RepositoryForkView.tsx"
     },
     "RepositoryShowObjectView": {
-      "hash": "057fe75366d6705671a24bac56d201581ac54567",
+      "hash": "3c5578449f0a838f9a8b679ec2360a212f5a4e4c",
       "pathSource": "./app/views/repository/RepositoryShowObjectView.tsx"
     },
     "RepositoryPullRequestCreateView": {

app/components/Button.styled.ts
@@ -1,7 +1,8 @@
 // 3rd-party
-import styled, { css } from "styled-components";
+import { css } from "styled-components";
+import styledContainerQuery from "styled-container-query";
 // app
-import { Colors } from "../utils/style";
+import { breakpoints, Colors } from "../utils/style";
 
 type ButtonProps = {
   disabled?: boolean;

...
@@ -50,6 +51,15 @@ const baseButtonCss = css<ButtonProps>`
     padding-top 140ms ease-in-out 0s,
     transform 140ms ease-in-out 0s;
 
+  &:container(max-width: 119px) {
+    font-size: 11px;
+    background: red !important;
+  }
+  &:container(min-width: 120px) {
+    font-size: 18px;
+    background: green !important;
+  }
+
   &:not(:disabled) {
     cursor: pointer;
   }

...
@@ -78,13 +88,17 @@ const baseButtonCss = css<ButtonProps>`
   }
 
   ${({ disabled }) => disabled && disabledButtonCss};
+
+  // @media only screen and (max-width: ${breakpoints.sm}) {
+  //   font-size: 11px;
+  // }
 `;
 
-export const Button = styled.button<ButtonProps>`
+export const Button = styledContainerQuery.button<ButtonProps>`
   ${baseButtonCss};
 `;
 
-export const ButtonAnchor = styled.a<ButtonProps>`
+export const ButtonAnchor = styledContainerQuery.a<ButtonProps>`
   ${baseButtonCss};
   text-decoration: none;
   &:hover {

app/components/DrawerPrimary.tsx
@@ -89,111 +89,101 @@ export const DrawerPrimary = ({
   }
 
   return (
-    <StyledDrawerPrimary id="drawer" themeScheme={themeScheme}>
-      <StyledDrawerHeader>
-        <StyledLogoArea themeScheme={themeScheme}>
-          <a href={"/"}>
-            <h1>{Const.APP_NAME}</h1>
-          </a>
-        </StyledLogoArea>
-      </StyledDrawerHeader>
-      <StyledDrawerContent>
-        <StyledDrawerListHeader style={{ margin: 0 }}>
-          <a href={buildRouteLink(AppRoute.ORGANIZATION_DETAILS, { orgSlug })}>
-            @{orgSlug}
-          </a>
-          <span>{" / "}</span>
-          <a
-            href={buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
-              orgSlug,
-              repoSlug,
-            })}
-          >
-            <TextEllipsis>{repoSlug}</TextEllipsis>
-          </a>
-        </StyledDrawerListHeader>
-        <StyledDrawerListHeader style={{ height: 32 }}>
-          <Grid.Row
-            fluid
-            nowrap
-            gap={8}
-            justifyContent={"center"}
-            alignItems={"center"}
-          >
-            <Grid.Row nowrap gap={4} alignItems={"center"}>
-              <GitPullIcon
-                color={NamedColors.TEXT_MUTED[themeScheme]}
-                size={16}
-              />
-              <span style={{ fontSize: 11 }}>Watchers</span>
-              <Chip
-                themeScheme={themeScheme}
-                color={NamedColors.TEXT_MUTED[themeScheme]}
-                style={{
-                  padding: "2px 6px",
-                  backgroundColor: "rgba(0,0,0,0.1)",
-                  fontSize: 11,
-                  color: NamedColors.TEXT_MUTED[themeScheme],
-                }}
-              >
-                {counters.watchers || "0"}
-              </Chip>
-            </Grid.Row>
-            <Grid.Row nowrap gap={4} alignItems={"center"}>
-              <LikeIcon color={NamedColors.TEXT_MUTED[themeScheme]} size={16} />
-              <span style={{ fontSize: 11 }}>Likes</span>
-              <Chip
-                themeScheme={themeScheme}
-                color={NamedColors.TEXT_MUTED[themeScheme]}
-                style={{
-                  padding: "2px 6px",
-                  backgroundColor: "rgba(0,0,0,0.1)",
-                  fontSize: 11,
-                  color: NamedColors.TEXT_MUTED[themeScheme],
-                }}
-              >
-                {counters.likes || "0"}
-              </Chip>
-            </Grid.Row>
-            <Grid.Row nowrap gap={4} alignItems={"center"}>
-              <GitForkIcon color={Colors.WHITE_01} size={16} />
-              <span style={{ fontSize: 11 }}>Forks</span>
-              <Chip
-                themeScheme={themeScheme}
-                color={NamedColors.TEXT_MUTED[themeScheme]}
-                style={{
-                  padding: "2px 6px",
-                  backgroundColor: "rgba(0,0,0,0.1)",
-                  fontSize: 11,
-                  color: NamedColors.TEXT_MUTED[themeScheme],
-                }}
-              >
-                {counters.forks || "0"}
-              </Chip>
+    <>
+      <StyledDrawerPrimary id="drawer" themeScheme={themeScheme}>
+        <StyledDrawerHeader>
+          <StyledLogoArea themeScheme={themeScheme}>
+            <a href={"/"}>
+              <h1>{Const.APP_NAME}</h1>
+            </a>
+          </StyledLogoArea>
+        </StyledDrawerHeader>
+        <StyledDrawerContent>
+          <StyledDrawerListHeader style={{ margin: 0 }}>
+            <a
+              href={buildRouteLink(AppRoute.ORGANIZATION_DETAILS, { orgSlug })}
+            >
+              @{orgSlug}
+            </a>
+            <span>{" / "}</span>
+            <a
+              href={buildRouteLink(AppRoute.REPOSITORY_DETAILS, {
+                orgSlug,
+                repoSlug,
+              })}
+            >
+              <TextEllipsis>{repoSlug}</TextEllipsis>
+            </a>
+          </StyledDrawerListHeader>
+          <StyledDrawerListHeader style={{ height: 32 }}>
+            <Grid.Row
+              fluid
+              nowrap
+              gap={8}
+              justifyContent={"center"}
+              alignItems={"center"}
+            >
+              <Grid.Row nowrap gap={4} alignItems={"center"}>
+                <GitPullIcon
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  size={16}
+                />
+                <span style={{ fontSize: 11 }}>Watchers</span>
+                <Chip
+                  themeScheme={themeScheme}
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  style={{
+                    padding: "2px 6px",
+                    backgroundColor: "rgba(0,0,0,0.1)",
+                    fontSize: 11,
+                    color: NamedColors.TEXT_MUTED[themeScheme],
+                  }}
+                >
+                  {counters.watchers || "0"}
+                </Chip>
+              </Grid.Row>
+              <Grid.Row nowrap gap={4} alignItems={"center"}>
+                <LikeIcon
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  size={16}
+                />
+                <span style={{ fontSize: 11 }}>Likes</span>
+                <Chip
+                  themeScheme={themeScheme}
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  style={{
+                    padding: "2px 6px",
+                    backgroundColor: "rgba(0,0,0,0.1)",
+                    fontSize: 11,
+                    color: NamedColors.TEXT_MUTED[themeScheme],
+                  }}
+                >
+                  {counters.likes || "0"}
+                </Chip>
+              </Grid.Row>
+              <Grid.Row nowrap gap={4} alignItems={"center"}>
+                <GitForkIcon color={Colors.WHITE_01} size={16} />
+                <span style={{ fontSize: 11 }}>Forks</span>
+                <Chip
+                  themeScheme={themeScheme}
+                  color={NamedColors.TEXT_MUTED[themeScheme]}
+                  style={{
+                    padding: "2px 6px",
+                    backgroundColor: "rgba(0,0,0,0.1)",
+                    fontSize: 11,
+                    color: NamedColors.TEXT_MUTED[themeScheme],
+                  }}
+                >
+                  {counters.forks || "0"}
+                </Chip>
+              </Grid.Row>
             </Grid.Row>
-          </Grid.Row>
-        </StyledDrawerListHeader>
-        <StyledDrawerList>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathFiles}
-            className={
-              ((!commonProps.path!.startsWith(pathPipelines) === false &&
-                commonProps.path!.startsWith(pathFiles)) ||
-                [pathFiles, pathRepo, pathRepoTrailing].some(
-                  (p) =>
-                    commonProps.path === p || commonProps.path!.startsWith(p),
-                )) &&
-              commonProps.path!.startsWith(pathPulls) === false &&
-              commonProps.path!.startsWith(pathPipelines) === false
-                ? "active"
-                : undefined
-            }
-            style={{ paddingRight: 16 }}
-          >
-            <span>Files</span>
-            <FolderIcon
-              color={
+          </StyledDrawerListHeader>
+          <StyledDrawerList>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathFiles}
+              className={
                 ((!commonProps.path!.startsWith(pathPipelines) === false &&
                   commonProps.path!.startsWith(pathFiles)) ||
                   [pathFiles, pathRepo, pathRepoTrailing].some(

...
@@ -202,87 +192,128 @@ export const DrawerPrimary = ({
                   )) &&
                 commonProps.path!.startsWith(pathPulls) === false &&
                 commonProps.path!.startsWith(pathPipelines) === false
-                  ? NamedColors.TEXT_DEFAULT[themeScheme]
-                  : NamedColors.TEXT_MUTED[themeScheme]
+                  ? "active"
+                  : undefined
+              }
+              style={{ paddingRight: 16 }}
+            >
+              <span>Files</span>
+              <FolderIcon
+                color={
+                  ((!commonProps.path!.startsWith(pathPipelines) === false &&
+                    commonProps.path!.startsWith(pathFiles)) ||
+                    [pathFiles, pathRepo, pathRepoTrailing].some(
+                      (p) =>
+                        commonProps.path === p ||
+                        commonProps.path!.startsWith(p),
+                    )) &&
+                  commonProps.path!.startsWith(pathPulls) === false &&
+                  commonProps.path!.startsWith(pathPipelines) === false
+                    ? NamedColors.TEXT_DEFAULT[themeScheme]
+                    : NamedColors.TEXT_MUTED[themeScheme]
+                }
+                size={16}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathPulls}
+              className={
+                commonProps.path! === pathPulls ||
+                commonProps.path!.startsWith(pathPulls)
+                  ? "active"
+                  : undefined
               }
-              size={16}
-            />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathPulls}
-            className={
-              commonProps.path! === pathPulls ||
-              commonProps.path!.startsWith(pathPulls)
-                ? "active"
-                : undefined
-            }
-          >
-            <span>Pull Requests</span>
-            <Chip themeScheme={themeScheme}>{counters.pulls || 0}</Chip>
-          </StyledDrawerListItem>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathPipelines}
-            className={
-              commonProps.path! === pathPipelines ||
-              commonProps.path!.startsWith(pathPipelines)
-                ? "active"
-                : undefined
-            }
-          >
-            <span>Pipelines</span>
-            <Chip themeScheme={themeScheme}>{counters.builds || 0}</Chip>
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Tests & Coverage</span>
-            <Chip themeScheme={themeScheme}>{counters.tests || 0}</Chip>
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Issues</span>
-            <Chip themeScheme={themeScheme}>{counters.issues || 0}</Chip>
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>API Reference</span>
-            <Chip themeScheme={themeScheme}>{counters.apiRefSymbols || 0}</Chip>
-          </StyledDrawerListItem>
-        </StyledDrawerList>
-        <StyledDrawerListHeader></StyledDrawerListHeader>
-        <StyledDrawerList></StyledDrawerList>
-      </StyledDrawerContent>
-      <StyledDrawerFooter>
-        <StyledDrawerList>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Feedback</span>
-            <CommentIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Help Center</span>
-            <span style={{ flex: 1 }}></span>
-            {counters.helpCenterNotifs! > 0 && (
-              <Chip themeScheme={themeScheme}>{counters.helpCenterNotifs}</Chip>
-            )}
-            <SupportIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Settings</span>
-            <SettingsIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-        </StyledDrawerList>
-      </StyledDrawerFooter>
-    </StyledDrawerPrimary>
+            >
+              <span>Pull Requests</span>
+              <Chip themeScheme={themeScheme}>{counters.pulls || 0}</Chip>
+            </StyledDrawerListItem>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathPipelines}
+              className={
+                commonProps.path! === pathPipelines ||
+                commonProps.path!.startsWith(pathPipelines)
+                  ? "active"
+                  : undefined
+              }
+            >
+              <span>Pipelines</span>
+              <Chip themeScheme={themeScheme}>{counters.builds || 0}</Chip>
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Tests & Coverage</span>
+              <Chip themeScheme={themeScheme}>{counters.tests || 0}</Chip>
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Issues</span>
+              <Chip themeScheme={themeScheme}>{counters.issues || 0}</Chip>
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>API Reference</span>
+              <Chip themeScheme={themeScheme}>
+                {counters.apiRefSymbols || 0}
+              </Chip>
+            </StyledDrawerListItem>
+          </StyledDrawerList>
+          <StyledDrawerListHeader></StyledDrawerListHeader>
+          <StyledDrawerList></StyledDrawerList>
+        </StyledDrawerContent>
+        <StyledDrawerFooter>
+          <StyledDrawerList>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Feedback</span>
+              <CommentIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Help Center</span>
+              <span style={{ flex: 1 }}></span>
+              {counters.helpCenterNotifs! > 0 && (
+                <Chip themeScheme={themeScheme}>
+                  {counters.helpCenterNotifs}
+                </Chip>
+              )}
+              <SupportIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Settings</span>
+              <SettingsIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+          </StyledDrawerList>
+        </StyledDrawerFooter>
+      </StyledDrawerPrimary>
+      <StyledDrawerOverlay href="#?"></StyledDrawerOverlay>
+    </>
   );
 };
 
+const StyledDrawerOverlay = styled.a`
+  display: none;
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: 21000;
+  pointer-events: none;
+  user-select: none;
+  background: rgba(0, 0, 0, 0.4);
+  filter: blur(8px);
+  opacity: 0;
+  transition: opacity 140ms ease-in-out 140ms;
+`;
+
 const StyledDrawerPrimary = styled.aside<
   WithThemeSchemeProp & { color?: string }
 >`

...
@@ -304,21 +335,30 @@ const StyledDrawerPrimary = styled.aside<
     background: ${NamedColors.HEADER[themeScheme]};
     border-right: 1px solid ${NamedColors.BORDER_DEFAULT[themeScheme]};
 
-    @media only screen and (max-width: 386px) {
-      display: none;
-
-      position: absolute;
-      top: 0;
-      left: 0;
-      bottom: 0;
-      z-index: 1000;
-      width: 85vw;
-      max-width: 320px;
-      transform: translateX(-100%);
+    transition: transform 140ms cubic-bezier(0, 0, 0.2, 1);
+
+    @media only screen and (max-width: 768px) {
+      & {
+        position: absolute;
+        top: 0;
+        left: 0;
+        bottom: 0;
+        z-index: 10000;
+        width: 85vw;
+        max-width: 320px;
+        transition: transform 140ms cubic-bezier(0, 0, 0.2, 1);
+        transform: translateX(-100%);
+      }
 
       &:target {
         transform: translateX(0);
-        transition: transform 0.3s ease-in-out;
+      }
+
+      &:target ~ ${StyledDrawerOverlay} {
+        display: flex;
+        pointer-events: auto;
+        opacity: 1;
+        z-index: 8000;
       }
     }
   `};

app/components/DrawerSettings.tsx
@@ -61,103 +61,132 @@ export const DrawerSettings = ({
   console.log("counters:", counters);
 
   return (
-    <StyledDrawerSettings id="drawer" themeScheme={themeScheme}>
-      <StyledDrawerHeader>
-        <StyledLogoArea themeScheme={themeScheme}>
-          <a href={"/"}>
-            <h1>{Const.APP_NAME}</h1>
-          </a>
-        </StyledLogoArea>
-      </StyledDrawerHeader>
-      <StyledDrawerContent>
-        <StyledDrawerListHeader>
-          <img
-            src={commonProps.currentUserAvatarUri || ""}
-            style={{
-              width: 20,
-              height: 20,
-              background: "red",
-              borderRadius: 16,
-              marginRight: 8,
-            }}
-          />
-          <a
-            href={buildRouteLink(
-              AppRoute.USER_DETAILS,
-              { username },
-              { encodeURIComponent: false },
-            )}
-          >
-            @{username}
-          </a>
-        </StyledDrawerListHeader>
-        <StyledDrawerList>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathMyAccount}
-            className={
-              // commonProps.path!.startsWith(pathMyAccount) ||
-              commonProps.path! === pathMyAccount ? "active" : undefined
-            }
-          >
-            <span>My Account</span>
-            <UserIcon color={NamedColors.TEXT_DEFAULT[themeScheme]} size={20} />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={pathSSHKeys}
-            className={
-              commonProps.path! === pathSSHKeys ||
-              commonProps.path!.startsWith(pathSSHKeys)
-                ? "active"
-                : undefined
-            }
-          >
-            <span>SSH Keys</span>
-            <Chip themeScheme={themeScheme} style={{ marginRight: 8 }}>
-              {counters.sshKeys || 0}
-            </Chip>
-            <KeyIcon color={NamedColors.TEXT_DEFAULT[themeScheme]} size={24} />
-          </StyledDrawerListItem>
-        </StyledDrawerList>
-        <StyledDrawerListHeader></StyledDrawerListHeader>
-        <StyledDrawerList></StyledDrawerList>
-      </StyledDrawerContent>
-      <StyledDrawerFooter>
-        <StyledDrawerList>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Feedback</span>
-            <CommentIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
+    <>
+      <StyledDrawerSettings id="drawer" themeScheme={themeScheme}>
+        <StyledDrawerHeader>
+          <StyledLogoArea themeScheme={themeScheme}>
+            <a href={"/"}>
+              <h1>{Const.APP_NAME}</h1>
+            </a>
+          </StyledLogoArea>
+        </StyledDrawerHeader>
+        <StyledDrawerContent>
+          <StyledDrawerListHeader>
+            <img
+              src={commonProps.currentUserAvatarUri || ""}
+              style={{
+                width: 20,
+                height: 20,
+                background: "red",
+                borderRadius: 16,
+                marginRight: 8,
+              }}
             />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem themeScheme={themeScheme} disabled>
-            <span>Help Center</span>
-            {counters.helpCenterNotifs! > 0 && (
-              <Chip themeScheme={themeScheme}>{counters.helpCenterNotifs}</Chip>
-            )}
-            <SupportIcon
-              color={NamedColors.TEXT_MUTED[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-          <StyledDrawerListItem
-            themeScheme={themeScheme}
-            href={buildRouteLink(AppRoute.AUTH_LOGOUT_ACTION, null)}
-          >
-            <span>Logout</span>
-            <LogOutIcon
-              color={NamedColors.TEXT_DEFAULT[themeScheme]}
-              size={20}
-            />
-          </StyledDrawerListItem>
-        </StyledDrawerList>
-      </StyledDrawerFooter>
-    </StyledDrawerSettings>
+            <a
+              href={buildRouteLink(
+                AppRoute.USER_DETAILS,
+                { username },
+                { encodeURIComponent: false },
+              )}
+            >
+              @{username}
+            </a>
+          </StyledDrawerListHeader>
+          <StyledDrawerList>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathMyAccount}
+              className={
+                // commonProps.path!.startsWith(pathMyAccount) ||
+                commonProps.path! === pathMyAccount ? "active" : undefined
+              }
+            >
+              <span>My Account</span>
+              <UserIcon
+                color={NamedColors.TEXT_DEFAULT[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={pathSSHKeys}
+              className={
+                commonProps.path! === pathSSHKeys ||
+                commonProps.path!.startsWith(pathSSHKeys)
+                  ? "active"
+                  : undefined
+              }
+            >
+              <span>SSH Keys</span>
+              <Chip themeScheme={themeScheme} style={{ marginRight: 8 }}>
+                {counters.sshKeys || 0}
+              </Chip>
+              <KeyIcon
+                color={NamedColors.TEXT_DEFAULT[themeScheme]}
+                size={24}
+              />
+            </StyledDrawerListItem>
+          </StyledDrawerList>
+          <StyledDrawerListHeader></StyledDrawerListHeader>
+          <StyledDrawerList></StyledDrawerList>
+        </StyledDrawerContent>
+        <StyledDrawerFooter>
+          <StyledDrawerList>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Feedback</span>
+              <CommentIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem themeScheme={themeScheme} disabled>
+              <span>Help Center</span>
+              {counters.helpCenterNotifs! > 0 && (
+                <Chip themeScheme={themeScheme}>
+                  {counters.helpCenterNotifs}
+                </Chip>
+              )}
+              <SupportIcon
+                color={NamedColors.TEXT_MUTED[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+            <StyledDrawerListItem
+              themeScheme={themeScheme}
+              href={buildRouteLink(AppRoute.AUTH_LOGOUT_ACTION, null)}
+            >
+              <span>Logout</span>
+              <LogOutIcon
+                color={NamedColors.TEXT_DEFAULT[themeScheme]}
+                size={20}
+              />
+            </StyledDrawerListItem>
+          </StyledDrawerList>
+        </StyledDrawerFooter>
+      </StyledDrawerSettings>
+      <StyledDrawerOverlay href="#?"></StyledDrawerOverlay>
+    </>
   );
 };
 
+const StyledDrawerOverlay = styled.a`
+  display: none;
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  width: 100vw;
+  height: 100vh;
+  z-index: 21000;
+  pointer-events: none;
+  user-select: none;
+  background: rgba(0, 0, 0, 0.4);
+  filter: blur(8px);
+  opacity: 0;
+  transition: opacity 140ms ease-in-out 140ms;
+`;
+
 const StyledDrawerSettings = styled.aside<
   WithThemeSchemeProp & { color?: string }
 >`