Website: Update Fleet premium trial page and signup modal (#35004)

Related to: https://github.com/fleetdm/fleet/issues/33798

Changes:
- Updated the position of the close button on the mobile signup modal.
- updated the signup modal to clear the form and errors when users
switch the displayed form.
- Updated the styles and spacing on the forgot password page to match
current website styles.
- Updated the mobile styles and spacing between elements on the Fleet
Premium trial page (/try).
This commit is contained in:
Eric 2025-10-30 12:49:00 -05:00 committed by GitHub
parent 93bea644ce
commit cd82d068f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 131 additions and 49 deletions

View file

@ -1,7 +1,7 @@
/**
* <signup-modal>
* -----------------------------------------------------------------------------
* A button with a built-in animated arrow
* A modal with a combined signup/login form
*
* @type {Component}
*
@ -25,7 +25,10 @@ parasails.registerComponent('signupModal', {
data: function (){
return {
formToDisplay: 'signup',
// Shared by forms
syncing: false,
cloudError: undefined,
// Signup form
signupFormData: {},
signupFormErrors: {},
signupFormRules: {
@ -34,15 +37,16 @@ parasails.registerComponent('signupModal', {
emailAddress: {required: true, isEmail: true},
password: {required: true},
},
cloudError: undefined,
// Login form
loginFormData: {},
loginFormErrors: {},
loginFormRules: {
emailAddress: {required: true, isEmail: true},
password: {required: true},
},
_bsModalIsAnimatingOut: false,
// For <modal>
_bsModalIsAnimatingOut: false,
originalScrollPosition: undefined,//« more on this below
};
},
@ -65,12 +69,12 @@ parasails.registerComponent('signupModal', {
<p class="mb-0">We just need a few details in order to get started.</p>
<div purpose="form-switch" class="d-flex flex-column">
<label purpose="form-option" class="form-control" :class="[formToDisplay === 'login' ? 'selected' : '']">
<input type="radio" v-model.trim="formToDisplay" value="login">
<input type="radio" v-model.trim="formToDisplay" value="login" @selected="switchForm('login')">
<span purpose="custom-radio"><span purpose="custom-radio-selected"></span></span>
I have an account
</label>
<label purpose="form-option" class="form-control" :class="[formToDisplay === 'signup' ? 'selected' : '']">
<input type="radio" v-model.trim="formToDisplay" value="signup">
<input type="radio" v-model.trim="formToDisplay" value="signup" @selected="switchForm('signup')">
<span purpose="custom-radio"><span purpose="custom-radio-selected"></span></span>
I don't have an account
</label>
@ -105,12 +109,10 @@ parasails.registerComponent('signupModal', {
</div>
</div>
<cloud-error v-if="cloudError==='emailAlreadyInUse'">
<p>This email is already linked to a Fleet account.<br> Please <a @click="formToDisplay = 'login'">sign in</a> with your email and password.</p>
This email is already linked to a Fleet account.<br> Please <a @click="formToDisplay = 'login'">sign in</a> with your email and password.
</cloud-error>
<cloud-error v-if="cloudError === 'invalidEmailDomain'">
<p>
Please enter your work or school email address.
</p>
</cloud-error>
<blockquote purpose="tip" v-if="cloudError === 'invalidEmailDomain'">
<img src="/images/icon-info-16x16@2x.png" alt="An icon indicating that this section has important information">
@ -140,6 +142,7 @@ parasails.registerComponent('signupModal', {
<div class="pb-3">
<ajax-button tabindex="3" :syncing="syncing" spinner="true" purpose="submit-button" class="btn-primary mt-4 btn-lg btn-block">Sign in</ajax-button>
</div>
<span class="text-center small"><a href="/customers/forgot-password">Forgot your password?</a></span>
</ajax-form>
</div>
</div><!-- /.modal-content -->
@ -194,6 +197,18 @@ parasails.registerComponent('signupModal', {
// $(this.$el).off('shown.bs.modal');
});//ƒ
},
watch: {
formToDisplay: function() {
// Reset forms and form errors when the form is switched.
this.signupFormData = {};
this.signupFormErrors = {};
this.loginFormData = {};
this.loginFormErrors = {};
this.cloudError = undefined;
}
},
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝

View file

@ -109,6 +109,18 @@
.small {
font-size: 12px;
}
a {
cursor: pointer;
line-height: 150%;
color: #515774;
text-decoration: underline;
text-underline-offset: 2px;
}
[parasails-component='cloud-error'] {
p {
color: @core-vibrant-red;
}
}
}
[purpose='submit-button'] {
@ -144,12 +156,8 @@
[purpose='modal-close-button'] {
.btn-reset();
opacity: 0.6;
padding-top: 8px;
padding-bottom: 8px;
padding-left: 16px;
padding-right: 16px;
top: 5px;
right: 0;
top: 25px;
right: 25px;
font-size: 28px;
line-height: 1;
&:hover {
@ -225,15 +233,6 @@
}
// // Modal backdrop styles are exposed globally here because this gets appended to the <body>
// .modal-backdrop {
// background-color: #192147;
// max-width: 100vw;
// max-height: 100vh;
// &.show {
// opacity: 0.25;
// }
// }
@media (max-width: 991px) {
.modal-backdrop.signup {
background-color: #FFF;
@ -247,17 +246,58 @@
[parasails-component='signup-modal'] {
max-width: 100vw;
-webkit-overflow-scrolling: touch;//« makes this actually scrollable on certain phones
[purpose='signup-modal'] {
margin: auto;
width: 100%;
}
[purpose='modal-dialog'] {
z-index: 100;
position: relative;
// max-width: 100%;
max-width: 100%;
[purpose='modal-content'] {
box-shadow: none;
margin-top: 48px;
width: 100%;
// max-width: 100%;
max-width: 100%;
padding: 48px;
border: none;
[purpose='modal-close-button'] {
top: -25px;
right: 25px;
}
}
}
}
}
@media (max-width: 576px) {
.modal-backdrop.signup {
background-color: #FFF;
&.show {
opacity: 1;
transition: none;
}
}
[parasails-component='signup-modal'] {
max-width: 100vw;
-webkit-overflow-scrolling: touch;//« makes this actually scrollable on certain phones
[purpose='signup-modal'] {
margin: auto;
width: 100%;
}
[purpose='modal-dialog'] {
z-index: 100;
position: relative;
max-width: 100%;
[purpose='modal-content'] {
box-shadow: none;
margin-top: 48px;
width: 100%;
max-width: 100%;
padding: 24px;
border: none;
}
}
}

View file

@ -3,6 +3,7 @@
h1 {
font-size: 28px;
line-height: 38px;
margin-bottom: 48px;
}
a {
color: @core-vibrant-blue;
@ -14,29 +15,33 @@
[purpose='customer-portal-form'] {
label {
margin-bottom: 8px;
color: var(--text-text-brand, #192147);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 700;
margin-bottom: 4px;
line-height: 21px; /* 150% */
}
input {
height: 40px;
border-radius: 4px;
border-radius: 6px;
}
.card {
border-radius: 6px;
}
.card-body {
padding: 2em;
padding: 24px;
}
}
[purpose='submit-button'] {
margin-left: auto;
margin-right: auto;
border-radius: 4px;
padding-top: 10px;
padding-bottom: 10px;
border-radius: 8px;
padding-top: 16px;
padding-bottom: 16px;
display: flex;
align-items: center;
span {
display: inline;
margin-left: auto;
@ -44,7 +49,7 @@
font-size: 16px;
line-height: 20px;
text-align: center;
font-weight: 400;
font-weight: 700;
}
}

View file

@ -26,21 +26,19 @@
line-height: 19.2px; /* 120% */
}
[purpose='modal'] {
p {
line-height: 24px;
}
[purpose='close-button'] {
position: relative;
}
[purpose='modal-close-button'] {
.btn-reset();
opacity: 0.6;
padding-top: 8px;
padding-bottom: 8px;
padding-left: 16px;
padding-right: 16px;
top: -100px;
right: -32px;
top: -78px;
right: -21px;
font-size: 28px;
line-height: 1;
&:hover {
@ -57,6 +55,7 @@
position: relative;
max-width: 600px;
margin: 0 auto;
min-width: 0px;
}
[purpose='modal-content'] {
width: 600px;
@ -71,7 +70,7 @@
img {
width: 32px;
height: auto;
margin-bottom: 40px;
margin-bottom: 24px;
}
}
[purpose='content'] {
@ -166,6 +165,7 @@
justify-content: center;
align-items: center;
// gap: 16px;
height: 200px;
flex: 1 0 0;
align-self: stretch;
border-radius: 12px;
@ -201,6 +201,7 @@
gap: 16px;
[purpose='modal-button'] {
padding: 24px;
height: 200px;
}
}
[purpose='license-icon'] {
@ -240,29 +241,50 @@
position: relative;
// max-width: 100%;
}
[purpose='modal'] {
max-width: unset;
}
[purpose='modal-close-button'] {
top: -128px;
right: -8px;
}
[purpose='modal-content'] {
position: relative;
box-shadow: none;
border: none;
margin-top: 48px;
width: 100%;
// max-width: 100%;
max-width: unset;
padding: 48px;
}
}
@media (max-width: 576px) {
[purpose='modal-button-links'] {
flex-direction: column;
gap: 24px;
}
[purpose='modal-alt-button-links'] {
flex-direction: column;
gap: 24px;
}
[purpose='modal-content'] {
box-shadow: none;
margin-top: 0px;
width: 100%;
padding: 48px;
padding: 24px;
}
[purpose='modal-close-button'] {
top: -66px;
right: -8px;
}
[purpose='content'] {
br {
display: none;
}
}
// Move close button further in.
}

View file

@ -11,7 +11,7 @@
<div class="invalid-feedback" v-if="formErrors.emailAddress">Please enter a valid email address.</div>
</div>
<cloud-error v-if="cloudError"></cloud-error>
<ajax-button purpose="submit-button" type="submit" :syncing="syncing" class="btn-info btn-lg btn-block">Reset password</ajax-button>
<ajax-button purpose="submit-button" type="submit" :syncing="syncing" class="btn-primary btn-block">Reset password</ajax-button>
</ajax-form>
</div>
</div>

View file

@ -3,7 +3,7 @@
<div purpose="page-container" class="container d-flex px-0">
<div purpose="page-container" class="container-fluid d-flex px-0">
<div purpose="modal" v-if="trialType === 'local trial'">
<div purpose="modal-content">
<div purpose="logo-row">
@ -46,7 +46,7 @@
<div purpose="content" v-else>
<div>
<h3>How was it?</h3>
<p>Your 30-day trial expired. Get a Premium license to keep everything unlocked, or continue with the free edition.</p>
<p class="mb-0">Your 30-day trial expired. Get a Premium license to keep everything unlocked, or continue with the free edition.</p>
</div>
<div purpose="modal-button-links">
<a purpose="modal-button" href="/new-license" no-underline>
@ -72,7 +72,7 @@
<div purpose="content">
<div>
<h3>How was it?</h3>
<p>
<p class="mb-0">
Your 30-day trial has finished.<br>
If you liked Fleet, heres how you can keep using it:
</p>