Fix login form layout on small screens (#10798)

Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
Artyom Savchenko
2026-04-24 11:56:02 +07:00
committed by GitHub
parent 69c9422d3b
commit f7ae635b21
7 changed files with 69 additions and 36 deletions
@@ -27,6 +27,7 @@
import BottomActionComponent from './BottomAction.svelte'
import Providers from './Providers.svelte'
import Tabs from './Tabs.svelte'
import { loginFormMinHeight, loginFormPadding } from '../loginFormLayout'
interface Action {
i18n: IntlString
@@ -125,8 +126,8 @@
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<form
class="container"
style:padding={$deviceInfo.docWidth <= 480 ? '.25rem 1.25rem' : '4rem 5rem'}
style:min-height={$deviceInfo.docHeight > 720 ? '42rem' : '0'}
style:padding={loginFormPadding($deviceInfo.docWidth, $deviceInfo.docHeight)}
style:min-height={loginFormMinHeight($deviceInfo.docHeight)}
on:keydown={(evt) => {
if (evt.key === 'Enter') {
evt.preventDefault()
@@ -225,7 +226,8 @@
<style lang="scss">
.container {
overflow: hidden;
overflow-x: hidden;
min-height: 0;
display: flex;
flex-direction: column;
@@ -252,31 +254,14 @@
grid-column-end: 3;
}
.hint {
margin-top: 1rem;
font-size: 0.8rem;
color: var(--theme-content-color);
}
.send {
margin-top: 0rem;
}
}
.grow-separator {
flex-grow: 1;
}
.footer {
margin-top: 1.75rem;
font-size: 0.8rem;
color: var(--theme-content-color);
span {
color: var(--theme-darker-color);
}
a {
font-weight: 500;
text-decoration: underline;
color: var(--theme-content-color);
}
}
}
</style>
@@ -42,6 +42,7 @@
import { logIn, workbenchId } from '@hcengineering/workbench'
import { onMount } from 'svelte'
import { loginAction, recoveryAction } from '../actions'
import { loginFormMinHeight, loginFormPadding } from '../loginFormLayout'
import login from '../plugin'
const location = getCurrentLocation()
@@ -259,8 +260,8 @@
{:else if showJoinWithAccount}
<div
class="join-with-account-container"
style:padding={$deviceInfo.docWidth <= 480 ? '.25rem 1.25rem' : '4rem 5rem'}
style:min-height={$deviceInfo.docHeight > 720 ? '42rem' : '0'}
style:padding={loginFormPadding($deviceInfo.docWidth, $deviceInfo.docHeight)}
style:min-height={loginFormMinHeight($deviceInfo.docHeight)}
>
<div class="join-with-account">
<div class="join-title">
@@ -16,8 +16,11 @@
<script lang="ts">
import { type IntlString, Severity, Status } from '@hcengineering/platform'
import { signupStore } from '@hcengineering/analytics-providers'
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import { onMount } from 'svelte'
import { loginFormPaddingInline } from '../loginFormLayout'
import { type BottomAction, doLoginAsGuest, doLoginNavigate, LoginMethods } from '../index'
import LoginPasswordForm from './LoginPasswordForm.svelte'
import LoginOtpForm from './LoginOtpForm.svelte'
@@ -88,7 +91,7 @@
{:else}
<LoginPasswordForm {navigateUrl} {signUpDisabled} {email} {caption} {subtitle} {onLogin} on:change={changeMethod} />
{/if}
<div class="actions">
<div class="actions" style:margin-inline-start={loginFormPaddingInline($deviceInfo.docWidth, $deviceInfo.docHeight)}>
<BottomActionComponent action={method === LoginMethods.Otp ? loginWithPasswordAction : loginWithCodeAction} />
<div class="login-as-guest">
<BottomActionComponent action={loginAsGuest} />
@@ -96,9 +99,6 @@
</div>
<style lang="scss">
.actions {
margin-left: 5rem;
}
.login-as-guest {
margin-top: 1rem;
}
@@ -21,6 +21,7 @@
import { LoginInfo } from '@hcengineering/account-client'
import Tabs from './Tabs.svelte'
import { loginFormMinHeight, loginFormPadding } from '../loginFormLayout'
import { BottomAction, doLoginNavigate, doValidateOtp, OtpLoginSteps, loginOtp } from '../index'
import login from '../plugin'
import BottomActionComponent from './BottomAction.svelte'
@@ -238,8 +239,8 @@
<form
bind:this={formElement}
class="container"
style:padding={$deviceInfo.docWidth <= 480 ? '.25rem 1.25rem' : '4rem 5rem'}
style:min-height={$deviceInfo.docHeight > 720 ? '42rem' : '0'}
style:padding={loginFormPadding($deviceInfo.docWidth, $deviceInfo.docHeight)}
style:min-height={loginFormMinHeight($deviceInfo.docHeight)}
>
<div class="header">
<Tabs {loginState} {signUpDisabled} />
@@ -334,7 +335,8 @@
}
.container {
overflow: hidden;
overflow-x: hidden;
min-height: 0;
display: flex;
flex-direction: column;
@@ -15,13 +15,14 @@
<script lang="ts">
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import { loginFormMinHeight, loginFormPadding } from '../loginFormLayout'
import Tabs from './Tabs.svelte'
import Providers from './Providers.svelte'
</script>
<div
style:padding={$deviceInfo.docWidth <= 480 ? '.25rem 1.25rem' : '4rem 5rem'}
style:min-height={$deviceInfo.docHeight > 720 ? '42rem' : '0'}
style:padding={loginFormPadding($deviceInfo.docWidth, $deviceInfo.docHeight)}
style:min-height={loginFormMinHeight($deviceInfo.docHeight)}
>
<Tabs loginState={'login'} signUpDisabled={true} />
<Providers />
@@ -17,7 +17,9 @@
import { OK, Severity, Status } from '@hcengineering/platform'
import { logIn } from '@hcengineering/workbench'
import { signupStore } from '@hcengineering/analytics-providers'
import { deviceOptionsStore as deviceInfo } from '@hcengineering/ui'
import { loginFormPaddingInline } from '../loginFormLayout'
import BottomActionComponent from './BottomAction.svelte'
import login from '../plugin'
import { getPasswordValidationRules } from '../validations'
@@ -146,7 +148,7 @@
{/if}
{#if useOTP}
<div class="action">
<div class="action" style:margin-inline-start={loginFormPaddingInline($deviceInfo.docWidth, $deviceInfo.docHeight)}>
<BottomActionComponent action={withPasswordAction} />
</div>
{:else}
@@ -154,10 +156,6 @@
{/if}
<style lang="scss">
.action {
margin-left: 5rem;
}
// TODO: Refactor me please
.placeholder {
height: 1.125rem;
@@ -0,0 +1,46 @@
//
// Copyright © 2026 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//
/** Padding for the form and the horizontal amount used to line up sibling rows (e.g. bottom links). */
export interface LoginFormLayout {
padding: string
/** Same as the horizontal part of `padding`; use for `margin-inline-start` on rows below the form. */
paddingInline: string
}
export function getLoginFormLayout (docWidth: number, docHeight: number): LoginFormLayout {
if (docWidth <= 480) {
return { padding: '.25rem 1.25rem', paddingInline: '1.25rem' }
}
if (docHeight <= 820) {
return { padding: '1rem 2rem', paddingInline: '2rem' }
}
return { padding: '4rem 5rem', paddingInline: '5rem' }
}
export function loginFormPadding (docWidth: number, docHeight: number): string {
return getLoginFormLayout(docWidth, docHeight).padding
}
export function loginFormPaddingInline (docWidth: number, docHeight: number): string {
return getLoginFormLayout(docWidth, docHeight).paddingInline
}
export function loginFormMinHeight (docHeight: number): string {
if (docHeight <= 880) {
return '0'
}
return 'min(42rem, calc(100dvh - 12rem))'
}