+
diff --git a/app/src/App.scss b/app/src/App.scss
index 9f501492..b25a1b6a 100644
--- a/app/src/App.scss
+++ b/app/src/App.scss
@@ -81,10 +81,12 @@
font-display: swap;
}
+// the minimum width is not applied globally, since the Home and auth pages reflow
+// down to mobile widths. the pages which are laid out for desktop widths apply it
+// themselves via the Layout component
html,
body,
#root {
background-color: #252f4a;
width: 100%;
- min-width: 1024px;
}
diff --git a/app/src/__tests__/store/appView.spec.ts b/app/src/__tests__/store/appView.spec.ts
index abeaac78..85144ee4 100644
--- a/app/src/__tests__/store/appView.spec.ts
+++ b/app/src/__tests__/store/appView.spec.ts
@@ -12,6 +12,17 @@ describe('AppView', () => {
store = rootStore.appView;
});
+ it('should only treat the Home page as responsive', () => {
+ store.goToHome();
+ expect(store.responsive).toBe(true);
+ store.goToPool();
+ expect(store.responsive).toBe(false);
+ store.goToLoop();
+ expect(store.responsive).toBe(false);
+ store.goToSettings();
+ expect(store.responsive).toBe(false);
+ });
+
it('should add an alert', async () => {
expect(store.alerts.size).toBe(0);
store.notify('test message', 'test title');
diff --git a/app/src/components/connect/QRCodeModal.tsx b/app/src/components/connect/QRCodeModal.tsx
index 51df4df8..154322ab 100644
--- a/app/src/components/connect/QRCodeModal.tsx
+++ b/app/src/components/connect/QRCodeModal.tsx
@@ -1,15 +1,25 @@
import React from 'react';
import styled from '@emotion/styled';
import { usePrefixedTranslation } from 'hooks';
-import QRCodeImg from 'qrcode.react';
+import { QRCodeSVG } from 'qrcode.react';
import { Paragraph } from 'components/base';
import Modal from 'components/common/Modal';
const Styled = {
QRWrap: styled.div`
display: inline-block;
+ max-width: 100%;
padding: 8px 8px 0;
background-color: ${props => props.theme.colors.white};
+
+ /* the code is rendered as an svg so that it can scale down to fit inside of the
+ modal on narrow screens, instead of being clipped by it */
+ > svg {
+ display: block;
+ width: 500px;
+ max-width: 100%;
+ height: auto;
+ }
`,
};
@@ -26,7 +36,7 @@ const QRCodeModal: React.FC = ({ url, visible, onClose }) => {
{l('desc')}
-
+
);
diff --git a/app/src/components/home/HomePage.tsx b/app/src/components/home/HomePage.tsx
index 0c1b1e88..b2023af7 100644
--- a/app/src/components/home/HomePage.tsx
+++ b/app/src/components/home/HomePage.tsx
@@ -3,24 +3,42 @@ import { observer } from 'mobx-react-lite';
import styled from '@emotion/styled';
import { usePrefixedTranslation } from 'hooks';
import { useStore } from 'store';
-import { BoltOutlined, Button, Display, Paragraph, QRCode } from 'components/base';
+import { AUTO_COLLAPSE_MAX_WIDTH } from 'store/stores/settingsStore';
+import { BoltOutlined, Button, QRCode } from 'components/base';
+import { Display, Paragraph } from 'components/common/v2/Text';
import PurpleButton from 'components/connect/PurpleButton';
import QRCodeModal from 'components/connect/QRCodeModal';
import YoutubeModal from './YoutubeModal';
const Styled = {
Wrapper: styled.div`
- padding: 72px 0;
+ /* while the sidebar is collapsed its toggle floats over the top of the page and
+ the layout reserves space for it, so only a small amount of padding is added on
+ top of that. the full padding is restored once the sidebar is displayed
+ alongside the content again */
+ padding: 16px 0 40px;
+
+ @media (min-width: ${AUTO_COLLAPSE_MAX_WIDTH + 1}px) {
+ padding: 72px 0;
+ }
+ `,
+ /* the connect buttons wrap onto multiple lines when the page is too narrow to fit
+ them all on one line */
+ Buttons: styled.div`
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16px 24px;
`,
PurpleButton: styled(PurpleButton)`
font-size: ${props => props.theme.sizes.s};
line-height: 24px;
padding: 8px 16px;
- margin-right: 24px;
`,
/* a text button which sits inline at the end of the description paragraph */
LearnMoreButton: styled(Button)`
font-family: ${props => props.theme.fonts.open.semiBold};
+ /* inherit the metrics of the paragraph, which change on smaller screens */
+ font-size: inherit;
line-height: inherit;
vertical-align: baseline;
padding: 0;
@@ -41,19 +59,19 @@ const HomePage: React.FC = () => {
const closeQRModal = useCallback(() => setQrUrl(''), []);
const toggleVideoModal = useCallback(() => setShowVideo(v => !v), []);
- const { Wrapper, PurpleButton, LearnMoreButton } = Styled;
+ const { Wrapper, Buttons, PurpleButton, LearnMoreButton } = Styled;
return (
{l('pageTitle')}
-
+
{l('connectDesc')}
{l('learnMore')}
-
+
{l('connectTerminalBtn')}
@@ -62,7 +80,7 @@ const HomePage: React.FC = () => {
{l('connectQrBtn')}
-
+
`
}
`;
+/** the width that the pages which do not reflow are laid out for */
+const DESKTOP_MIN_WIDTH = 1024;
+
/** the space the sidebar toggle occupies at the top of the page, including its offset */
const HAMBURGER_HEIGHT = 80;
@@ -65,12 +68,15 @@ const HAMBURGER_HEIGHT = 80;
const SIDEBAR_WIDTH = 285;
const Styled = {
- Container: styled.div<{ fullWidth: boolean }>`
+ Container: styled.div<{ fullWidth: boolean; responsive: boolean }>`
position: relative;
height: 100%;
max-width: ${props => (props.fullWidth ? '100%' : '1440px')};
width: ${props => (props.fullWidth ? '100%' : '100%')};
margin: 0 auto;
+
+ /* pages which do not reflow scroll horizontally rather than being squeezed */
+ ${props => !props.responsive && `min-width: ${DESKTOP_MIN_WIDTH}px;`}
`,
Hamburger: styled.span`
display: inline-block;
@@ -163,7 +169,7 @@ export const Layout: React.FC = ({ children }) => {
-
+ {children}
diff --git a/app/src/store/views/appView.ts b/app/src/store/views/appView.ts
index b4985d4b..4c3042df 100644
--- a/app/src/store/views/appView.ts
+++ b/app/src/store/views/appView.ts
@@ -32,6 +32,15 @@ export default class AppView {
return this._store.router.location.pathname === `${PUBLIC_URL}/pool`;
}
+ /**
+ * indicates if the current page reflows down to mobile widths. the remaining pages
+ * contain tables and multi column sections which are laid out for desktop widths, so
+ * they keep a minimum width and scroll horizontally instead of being squeezed
+ */
+ get responsive() {
+ return this._store.router.location.pathname === `${PUBLIC_URL}/home`;
+ }
+
/** navigate to the specified route */
goTo(route: string) {
const path = `${PUBLIC_URL}${route}`;