@vaadin/login 25.0.0-alpha2 → 25.0.0-alpha21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/package.json +14 -14
  2. package/src/styles/vaadin-login-form-wrapper-base-styles.js +65 -0
  3. package/src/styles/vaadin-login-overlay-wrapper-base-styles.js +52 -0
  4. package/src/title-controller.d.ts +11 -0
  5. package/src/title-controller.js +100 -0
  6. package/src/vaadin-login-form-mixin.js +93 -17
  7. package/src/vaadin-login-form-wrapper.js +16 -24
  8. package/src/vaadin-login-form.d.ts +2 -5
  9. package/src/vaadin-login-form.js +27 -64
  10. package/src/vaadin-login-mixin.d.ts +1 -1
  11. package/src/vaadin-login-mixin.js +1 -1
  12. package/src/vaadin-login-overlay-mixin.d.ts +1 -3
  13. package/src/vaadin-login-overlay-mixin.js +60 -87
  14. package/src/vaadin-login-overlay-wrapper.js +32 -12
  15. package/src/vaadin-login-overlay.d.ts +29 -19
  16. package/src/vaadin-login-overlay.js +70 -45
  17. package/vaadin-login-form.js +1 -1
  18. package/vaadin-login-overlay.js +1 -1
  19. package/web-types.json +9 -27
  20. package/web-types.lit.json +12 -12
  21. package/src/vaadin-login-form-wrapper-styles.js +0 -32
  22. package/src/vaadin-login-overlay-wrapper-mixin.js +0 -78
  23. package/src/vaadin-login-overlay-wrapper-styles.js +0 -35
  24. package/theme/lumo/vaadin-login-form-wrapper-styles.d.ts +0 -1
  25. package/theme/lumo/vaadin-login-form-wrapper-styles.js +0 -94
  26. package/theme/lumo/vaadin-login-form.d.ts +0 -5
  27. package/theme/lumo/vaadin-login-form.js +0 -5
  28. package/theme/lumo/vaadin-login-overlay-styles.d.ts +0 -1
  29. package/theme/lumo/vaadin-login-overlay-styles.js +0 -187
  30. package/theme/lumo/vaadin-login-overlay.d.ts +0 -6
  31. package/theme/lumo/vaadin-login-overlay.js +0 -6
  32. /package/src/{vaadin-login-form-wrapper-styles.d.ts → styles/vaadin-login-form-wrapper-base-styles.d.ts} +0 -0
  33. /package/src/{vaadin-login-overlay-wrapper-styles.d.ts → styles/vaadin-login-overlay-wrapper-base-styles.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/login",
3
- "version": "25.0.0-alpha2",
3
+ "version": "25.0.0-alpha21",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,7 +21,6 @@
21
21
  "type": "module",
22
22
  "files": [
23
23
  "src",
24
- "theme",
25
24
  "vaadin-*.d.ts",
26
25
  "vaadin-*.js",
27
26
  "web-types.json",
@@ -35,25 +34,26 @@
35
34
  ],
36
35
  "dependencies": {
37
36
  "@open-wc/dedupe-mixin": "^1.3.0",
38
- "@vaadin/button": "25.0.0-alpha2",
39
- "@vaadin/component-base": "25.0.0-alpha2",
40
- "@vaadin/overlay": "25.0.0-alpha2",
41
- "@vaadin/password-field": "25.0.0-alpha2",
42
- "@vaadin/text-field": "25.0.0-alpha2",
43
- "@vaadin/vaadin-lumo-styles": "25.0.0-alpha2",
44
- "@vaadin/vaadin-themable-mixin": "25.0.0-alpha2",
37
+ "@vaadin/button": "25.0.0-alpha21",
38
+ "@vaadin/component-base": "25.0.0-alpha21",
39
+ "@vaadin/overlay": "25.0.0-alpha21",
40
+ "@vaadin/password-field": "25.0.0-alpha21",
41
+ "@vaadin/text-field": "25.0.0-alpha21",
42
+ "@vaadin/vaadin-themable-mixin": "25.0.0-alpha21",
45
43
  "lit": "^3.0.0"
46
44
  },
47
45
  "devDependencies": {
48
- "@vaadin/chai-plugins": "25.0.0-alpha2",
49
- "@vaadin/checkbox": "25.0.0-alpha2",
50
- "@vaadin/test-runner-commands": "25.0.0-alpha2",
46
+ "@vaadin/a11y-base": "25.0.0-alpha21",
47
+ "@vaadin/chai-plugins": "25.0.0-alpha21",
48
+ "@vaadin/checkbox": "25.0.0-alpha21",
49
+ "@vaadin/test-runner-commands": "25.0.0-alpha21",
51
50
  "@vaadin/testing-helpers": "^2.0.0",
52
- "sinon": "^18.0.0"
51
+ "@vaadin/vaadin-lumo-styles": "25.0.0-alpha21",
52
+ "sinon": "^21.0.0"
53
53
  },
54
54
  "web-types": [
55
55
  "web-types.json",
56
56
  "web-types.lit.json"
57
57
  ],
58
- "gitHead": "67ffcd5355cf21ce1b5039c598525109fc4c164b"
58
+ "gitHead": "8fb9e9710c01449edf623a1aaac4655cdc11a933"
59
59
  }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2018 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import '@vaadin/component-base/src/styles/style-props.js';
7
+ import { css } from 'lit';
8
+
9
+ export const loginFormWrapperStyles = css`
10
+ :host {
11
+ background: var(--vaadin-login-form-background, transparent);
12
+ border-radius: var(--vaadin-login-form-border-radius, 0);
13
+ display: flex;
14
+ box-sizing: border-box;
15
+ flex-direction: column;
16
+ gap: var(--vaadin-login-form-gap, var(--vaadin-gap-l));
17
+ padding: var(--vaadin-login-form-padding, var(--vaadin-padding-l));
18
+ max-width: 100%;
19
+ width: var(--vaadin-login-form-width, 360px);
20
+ }
21
+
22
+ :host([hidden]) {
23
+ display: none !important;
24
+ }
25
+
26
+ ::slotted(form) {
27
+ display: flex;
28
+ flex-direction: column;
29
+ gap: var(--vaadin-login-form-gap, var(--vaadin-gap-m));
30
+ }
31
+
32
+ ::slotted([slot='form-title']) {
33
+ color: var(--vaadin-login-form-title-color, var(--vaadin-text-color));
34
+ font-size: var(--vaadin-login-form-title-font-size, 1.25rem);
35
+ font-weight: var(--vaadin-login-form-title-font-weight, 600);
36
+ line-height: var(--vaadin-login-form-title-line-height, inherit);
37
+ }
38
+
39
+ :host([error]) [part='error-message'] {
40
+ display: grid;
41
+ }
42
+
43
+ [part='error-message'] {
44
+ color: var(--vaadin-login-form-error-color, var(--vaadin-text-color));
45
+ font-size: var(--vaadin-login-form-error-font-size, inherit);
46
+ font-weight: var(--vaadin-login-form-error-font-weight, 400);
47
+ gap: var(--vaadin-login-form-error-gap, 0 var(--vaadin-gap-s));
48
+ grid-template-columns: auto 1fr;
49
+ line-height: var(--vaadin-login-form-error-line-height, inherit);
50
+ }
51
+
52
+ [part='error-message']::before {
53
+ background: currentColor;
54
+ content: '';
55
+ display: inline-block;
56
+ flex: none;
57
+ height: var(--vaadin-icon-size, 1lh);
58
+ mask: var(--_vaadin-icon-warn) 50% / var(--vaadin-icon-visual-size, 100%) no-repeat;
59
+ width: var(--vaadin-icon-size, 1lh);
60
+ }
61
+
62
+ [part='error-message-description'] {
63
+ grid-column: 2;
64
+ }
65
+ `;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2018 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import '@vaadin/component-base/src/styles/style-props.js';
7
+ import { css } from 'lit';
8
+ import { overlayStyles } from '@vaadin/overlay/src/styles/vaadin-overlay-base-styles.js';
9
+
10
+ const loginOverlayWrapper = css`
11
+ [part='overlay'] {
12
+ background: var(
13
+ --vaadin-login-overlay-background,
14
+ var(--vaadin-overlay-background, var(--vaadin-background-color))
15
+ );
16
+ border: var(--vaadin-login-overlay-border-width, var(--vaadin-overlay-border-width, 1px)) solid
17
+ var(--vaadin-login-overlay-border-color, var(--vaadin-overlay-border-color, var(--vaadin-border-color-secondary)));
18
+ border-radius: var(--vaadin-login-overlay-border-radius, var(--vaadin-radius-l));
19
+ box-shadow: var(--vaadin-login-overlay-shadow, var(--vaadin-overlay-shadow, 0 8px 24px -4px rgba(0, 0, 0, 0.3)));
20
+ }
21
+
22
+ [part='card'] {
23
+ box-sizing: border-box;
24
+ display: flex;
25
+ flex-direction: column;
26
+ max-width: 100%;
27
+ overflow: hidden;
28
+ }
29
+
30
+ [part='brand'] {
31
+ background: var(--vaadin-login-overlay-brand-background, var(--vaadin-background-container));
32
+ display: flex;
33
+ flex-direction: column;
34
+ padding: var(--vaadin-login-overlay-brand-padding, var(--vaadin-padding-l));
35
+ }
36
+
37
+ ::slotted([slot='title']) {
38
+ color: var(--vaadin-login-overlay-title-color, var(--vaadin-text-color));
39
+ font-size: var(--vaadin-login-overlay-title-font-size, inherit);
40
+ font-weight: var(--vaadin-login-overlay-title-font-weight, 600);
41
+ line-height: var(--vaadin-login-overlay-title-line-height, inherit);
42
+ }
43
+
44
+ [part='description'] {
45
+ color: var(--vaadin-login-overlay-description-color, var(--vaadin-text-color-secondary));
46
+ font-size: var(--vaadin-login-overlay-description-font-size, inherit);
47
+ font-weight: var(--vaadin-login-overlay-description-font-weight, inherit);
48
+ line-height: var(--vaadin-login-overlay-description-line-height, inherit);
49
+ }
50
+ `;
51
+
52
+ export const loginOverlayWrapperStyles = [overlayStyles, loginOverlayWrapper];
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2018 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { SlotChildObserveController } from '@vaadin/component-base/src/slot-child-observe-controller.js';
7
+
8
+ /**
9
+ * A controller to manage the title element.
10
+ */
11
+ export class TitleController extends SlotChildObserveController {}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2018 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { SlotChildObserveController } from '@vaadin/component-base/src/slot-child-observe-controller.js';
7
+
8
+ /**
9
+ * A controller to manage the title element.
10
+ */
11
+ export class TitleController extends SlotChildObserveController {
12
+ constructor(host) {
13
+ super(host, 'title', 'div');
14
+ }
15
+
16
+ /**
17
+ * Set title based on corresponding host property.
18
+ *
19
+ * @param {string} title
20
+ */
21
+ setTitle(title) {
22
+ this.title = title;
23
+
24
+ // Restore the default title, if needed.
25
+ const titleNode = this.getSlotChild();
26
+ if (!titleNode) {
27
+ this.restoreDefaultNode();
28
+ }
29
+
30
+ // When default title is used, update it.
31
+ if (this.node === this.defaultNode) {
32
+ this.updateDefaultNode(this.node);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Set level based on corresponding host property.
38
+ *
39
+ * @param {string} level
40
+ */
41
+ setLevel(level) {
42
+ this.level = level;
43
+
44
+ // When default title is used, update it.
45
+ if (this.node === this.defaultNode) {
46
+ this.updateDefaultNode(this.node);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Override method inherited from `SlotController`
52
+ * to customize heading on the default title node.
53
+ *
54
+ * @param {Node} node
55
+ * @protected
56
+ * @override
57
+ */
58
+ initNode(node) {
59
+ if (node === this.defaultNode) {
60
+ node.setAttribute('role', 'heading');
61
+ }
62
+
63
+ this.host.setAttribute('aria-labelledby', node.id);
64
+ }
65
+
66
+ /**
67
+ * Override method inherited from `SlotChildObserveController`
68
+ * to restore the default title element.
69
+ *
70
+ * @protected
71
+ * @override
72
+ */
73
+ restoreDefaultNode() {
74
+ const { title } = this;
75
+
76
+ // Restore the default title.
77
+ if (title && title.trim() !== '') {
78
+ const node = this.attachDefaultNode();
79
+ this.initNode(node);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Override method inherited from `SlotChildObserveController`
85
+ * to update the default title element.
86
+ *
87
+ * @param {Node | undefined} node
88
+ * @protected
89
+ * @override
90
+ */
91
+ updateDefaultNode(node) {
92
+ if (node) {
93
+ node.textContent = this.title;
94
+ node.setAttribute('aria-level', this.level);
95
+ }
96
+
97
+ // Notify the host after update.
98
+ super.updateDefaultNode(node);
99
+ }
100
+ }
@@ -3,6 +3,8 @@
3
3
  * Copyright (c) 2018 - 2025 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
+ import { html, render } from 'lit';
7
+ import { ifDefined } from 'lit/directives/if-defined.js';
6
8
  import { LoginMixin } from './vaadin-login-mixin.js';
7
9
 
8
10
  function isCheckbox(field) {
@@ -15,16 +17,43 @@ function isCheckbox(field) {
15
17
  */
16
18
  export const LoginFormMixin = (superClass) =>
17
19
  class LoginFormMixin extends LoginMixin(superClass) {
18
- static get observers() {
19
- return ['_errorChanged(error)'];
20
- }
21
-
20
+ /** @protected */
22
21
  get _customFields() {
23
- return [...this.$.vaadinLoginFormWrapper.children].filter((node) => {
22
+ return [...this.children].filter((node) => {
24
23
  return node.getAttribute('slot') === 'custom-form-area' && node.hasAttribute('name');
25
24
  });
26
25
  }
27
26
 
27
+ /** @protected */
28
+ get _userNameField() {
29
+ return this.querySelector('#vaadinLoginUsername');
30
+ }
31
+
32
+ /** @protected */
33
+ get _passwordField() {
34
+ return this.querySelector('#vaadinLoginPassword');
35
+ }
36
+
37
+ /**
38
+ * Override update to render slotted form and buttons
39
+ * into light DOM after rendering shadow DOM.
40
+ * @protected
41
+ */
42
+ update(props) {
43
+ super.update(props);
44
+
45
+ this.__renderSlottedForm();
46
+ }
47
+
48
+ /** @protected */
49
+ updated(props) {
50
+ super.updated(props);
51
+
52
+ if (props.has('error') && this.error && !this._preventAutoEnable) {
53
+ this.disabled = false;
54
+ }
55
+ }
56
+
28
57
  /** @protected */
29
58
  async connectedCallback() {
30
59
  super.connectedCallback();
@@ -32,23 +61,70 @@ export const LoginFormMixin = (superClass) =>
32
61
  if (!this.noAutofocus) {
33
62
  // Wait for the form to fully render.
34
63
  await new Promise(requestAnimationFrame);
35
- this.$.vaadinLoginUsername.focus();
64
+ this._userNameField.focus();
36
65
  }
37
66
  }
38
67
 
39
- /** @private */
40
- _errorChanged() {
41
- if (this.error && !this._preventAutoEnable) {
42
- this.disabled = false;
43
- }
68
+ __renderSlottedForm() {
69
+ render(
70
+ html`
71
+ <form method="POST" action="${ifDefined(this.action)}" @formdata="${this._onFormData}" slot="form">
72
+ <input id="csrf" type="hidden" />
73
+ <vaadin-text-field
74
+ name="username"
75
+ .label="${this.__effectiveI18n.form.username}"
76
+ .errorMessage="${this.__effectiveI18n.errorMessage.username}"
77
+ id="vaadinLoginUsername"
78
+ required
79
+ @keydown="${this._handleInputKeydown}"
80
+ autocapitalize="none"
81
+ autocorrect="off"
82
+ spellcheck="false"
83
+ autocomplete="username"
84
+ manual-validation
85
+ >
86
+ <input type="text" slot="input" @keyup="${this._handleInputKeyup}" />
87
+ </vaadin-text-field>
88
+
89
+ <vaadin-password-field
90
+ name="password"
91
+ .label="${this.__effectiveI18n.form.password}"
92
+ .errorMessage="${this.__effectiveI18n.errorMessage.password}"
93
+ id="vaadinLoginPassword"
94
+ required
95
+ @keydown="${this._handleInputKeydown}"
96
+ spellcheck="false"
97
+ autocomplete="current-password"
98
+ manual-validation
99
+ >
100
+ <input type="password" slot="input" @keyup="${this._handleInputKeyup}" />
101
+ </vaadin-password-field>
102
+ </form>
103
+
104
+ <vaadin-button slot="submit" theme="primary submit" @click="${this.submit}" .disabled="${this.disabled}">
105
+ ${this.__effectiveI18n.form.submit}
106
+ </vaadin-button>
107
+
108
+ <vaadin-button
109
+ slot="forgot-password"
110
+ theme="tertiary small"
111
+ @click="${this._onForgotPasswordClick}"
112
+ ?hidden="${this.noForgotPassword}"
113
+ >
114
+ ${this.__effectiveI18n.form.forgotPassword}
115
+ </vaadin-button>
116
+ `,
117
+ this,
118
+ { host: this },
119
+ );
44
120
  }
45
121
 
46
122
  /**
47
123
  * Submits the form.
48
124
  */
49
125
  submit() {
50
- const userName = this.$.vaadinLoginUsername;
51
- const password = this.$.vaadinLoginPassword;
126
+ const userName = this._userNameField;
127
+ const password = this._passwordField;
52
128
 
53
129
  // eslint-disable-next-line no-restricted-syntax
54
130
  userName.validate();
@@ -91,8 +167,9 @@ export const LoginFormMixin = (superClass) =>
91
167
  const csrfMetaName = document.querySelector('meta[name=_csrf_parameter]');
92
168
  const csrfMetaValue = document.querySelector('meta[name=_csrf]');
93
169
  if (csrfMetaName && csrfMetaValue) {
94
- this.$.csrf.name = csrfMetaName.content;
95
- this.$.csrf.value = csrfMetaValue.content;
170
+ const csrf = this.querySelector('#csrf');
171
+ csrf.name = csrfMetaName.content;
172
+ csrf.value = csrfMetaValue.content;
96
173
  }
97
174
  this.querySelector('form').submit();
98
175
  }
@@ -117,8 +194,7 @@ export const LoginFormMixin = (superClass) =>
117
194
  _handleInputKeydown(e) {
118
195
  if (e.key === 'Enter') {
119
196
  const { currentTarget: inputActive } = e;
120
- const nextInput =
121
- inputActive.id === 'vaadinLoginUsername' ? this.$.vaadinLoginPassword : this.$.vaadinLoginUsername;
197
+ const nextInput = inputActive.id === 'vaadinLoginUsername' ? this._passwordField : this._userNameField;
122
198
  // eslint-disable-next-line no-restricted-syntax
123
199
  if (inputActive.validate()) {
124
200
  if (nextInput.checkValidity()) {
@@ -6,8 +6,9 @@
6
6
  import { html, LitElement } from 'lit';
7
7
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
8
8
  import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
9
+ import { LumoInjectionMixin } from '@vaadin/vaadin-themable-mixin/lumo-injection-mixin.js';
9
10
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
10
- import { loginFormWrapperStyles } from './vaadin-login-form-wrapper-styles.js';
11
+ import { loginFormWrapperStyles } from './styles/vaadin-login-form-wrapper-base-styles.js';
11
12
 
12
13
  /**
13
14
  * An element used internally by `<vaadin-login-form>`. Not intended to be used separately.
@@ -16,7 +17,7 @@ import { loginFormWrapperStyles } from './vaadin-login-form-wrapper-styles.js';
16
17
  * @mixes ThemableMixin
17
18
  * @private
18
19
  */
19
- class LoginFormWrapper extends ThemableMixin(PolylitMixin(LitElement)) {
20
+ class LoginFormWrapper extends ThemableMixin(PolylitMixin(LumoInjectionMixin(LitElement))) {
20
21
  static get is() {
21
22
  return 'vaadin-login-form-wrapper';
22
23
  }
@@ -44,39 +45,30 @@ class LoginFormWrapper extends ThemableMixin(PolylitMixin(LitElement)) {
44
45
  i18n: {
45
46
  type: Object,
46
47
  },
47
-
48
- /**
49
- * Used to customize the `aria-level` attribute on the heading element.
50
- */
51
- headingLevel: {
52
- type: Number,
53
- },
54
48
  };
55
49
  }
56
50
 
57
51
  /** @protected */
58
52
  render() {
59
53
  return html`
60
- <section part="form">
61
- <div part="form-title" role="heading" aria-level="${this.headingLevel}">${this.i18n.form.title}</div>
62
- <div part="error-message" ?hidden="${!this.error}">
63
- <strong part="error-message-title">${this.i18n.errorMessage.title}</strong>
64
- <p part="error-message-description">${this.i18n.errorMessage.message}</p>
65
- </div>
54
+ <slot name="form-title"></slot>
55
+ <div part="error-message" ?hidden="${!this.error}">
56
+ <strong part="error-message-title">${this.i18n.errorMessage.title}</strong>
57
+ <div part="error-message-description">${this.i18n.errorMessage.message}</div>
58
+ </div>
66
59
 
67
- <slot name="form"></slot>
60
+ <slot name="form"></slot>
68
61
 
69
- <slot name="custom-form-area"></slot>
62
+ <slot name="custom-form-area"></slot>
70
63
 
71
- <slot name="submit"></slot>
64
+ <slot name="submit"></slot>
72
65
 
73
- <slot name="forgot-password"></slot>
66
+ <slot name="forgot-password"></slot>
74
67
 
75
- <div part="footer">
76
- <slot name="footer"></slot>
77
- <p>${this.i18n.additionalInformation}</p>
78
- </div>
79
- </section>
68
+ <div part="footer">
69
+ <slot name="footer"></slot>
70
+ <div>${this.i18n.additionalInformation}</div>
71
+ </div>
80
72
  `;
81
73
  }
82
74
  }
@@ -39,7 +39,7 @@ export interface LoginFormEventMap extends HTMLElementEventMap, LoginFormCustomE
39
39
  * `<vaadin-login-form>` is a Web Component providing an easy way to require users
40
40
  * to log in into an application. Note that component has no shadowRoot.
41
41
  *
42
- * ```
42
+ * ```html
43
43
  * <vaadin-login-form></vaadin-login-form>
44
44
  * ```
45
45
  *
@@ -48,10 +48,7 @@ export interface LoginFormEventMap extends HTMLElementEventMap, LoginFormCustomE
48
48
  *
49
49
  * ### Styling
50
50
  *
51
- * The component doesn't have a shadowRoot, so the `<form>` and input fields can be styled from a global scope.
52
- *
53
- * Use `<vaadin-login-form-wrapper>` themable component to apply styles.
54
- * The following shadow DOM parts of the `<vaadin-login-form-wrapper>` are available for styling:
51
+ * The following shadow DOM parts are available for styling:
55
52
  *
56
53
  * Part name | Description
57
54
  * ---------------|---------------------------------------------------------|
@@ -7,7 +7,7 @@ import '@vaadin/button/src/vaadin-button.js';
7
7
  import '@vaadin/text-field/src/vaadin-text-field.js';
8
8
  import '@vaadin/password-field/src/vaadin-password-field.js';
9
9
  import './vaadin-login-form-wrapper.js';
10
- import { html, LitElement } from 'lit';
10
+ import { css, html, LitElement } from 'lit';
11
11
  import { ifDefined } from 'lit/directives/if-defined.js';
12
12
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
13
13
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
@@ -19,7 +19,7 @@ import { LoginFormMixin } from './vaadin-login-form-mixin.js';
19
19
  * `<vaadin-login-form>` is a Web Component providing an easy way to require users
20
20
  * to log in into an application. Note that component has no shadowRoot.
21
21
  *
22
- * ```
22
+ * ```html
23
23
  * <vaadin-login-form></vaadin-login-form>
24
24
  * ```
25
25
  *
@@ -28,10 +28,7 @@ import { LoginFormMixin } from './vaadin-login-form-mixin.js';
28
28
  *
29
29
  * ### Styling
30
30
  *
31
- * The component doesn't have a shadowRoot, so the `<form>` and input fields can be styled from a global scope.
32
- * Use `<vaadin-login-form-wrapper>` themable component to apply styles.
33
- *
34
- * The following shadow DOM parts of the `<vaadin-login-form-wrapper>` are available for styling:
31
+ * The following shadow DOM parts are available for styling:
35
32
  *
36
33
  * Part name | Description
37
34
  * ---------------|---------------------------------------------------------|
@@ -60,74 +57,40 @@ class LoginForm extends LoginFormMixin(ElementMixin(ThemableMixin(PolylitMixin(L
60
57
  return 'vaadin-login-form';
61
58
  }
62
59
 
63
- /**
64
- * @protected
65
- * @override
66
- */
67
- createRenderRoot() {
68
- return this;
60
+ static get styles() {
61
+ return css`
62
+ :host {
63
+ display: block;
64
+ max-width: 100%;
65
+ }
66
+
67
+ :host([hidden]) {
68
+ display: none !important;
69
+ }
70
+ `;
69
71
  }
70
72
 
71
73
  /** @protected */
72
74
  render() {
73
75
  return html`
74
- <style>
75
- vaadin-login-form-wrapper > form > * {
76
- width: 100%;
77
- }
78
- </style>
79
76
  <vaadin-login-form-wrapper
80
- id="vaadinLoginFormWrapper"
77
+ id="form"
81
78
  theme="${ifDefined(this._theme)}"
82
79
  .error="${this.error}"
83
80
  .i18n="${this.__effectiveI18n}"
84
- .headingLevel="${this.headingLevel}"
81
+ part="form"
82
+ role="region"
83
+ aria-labelledby="title"
84
+ exportparts="error-message, error-message-title, error-message-description, footer"
85
85
  >
86
- <form method="POST" action="${ifDefined(this.action)}" @formdata="${this._onFormData}" slot="form">
87
- <input id="csrf" type="hidden" />
88
- <vaadin-text-field
89
- name="username"
90
- .label="${this.__effectiveI18n.form.username}"
91
- .errorMessage="${this.__effectiveI18n.errorMessage.username}"
92
- id="vaadinLoginUsername"
93
- required
94
- @keydown="${this._handleInputKeydown}"
95
- autocapitalize="none"
96
- autocorrect="off"
97
- spellcheck="false"
98
- autocomplete="username"
99
- manual-validation
100
- >
101
- <input type="text" slot="input" @keyup="${this._handleInputKeyup}" />
102
- </vaadin-text-field>
103
-
104
- <vaadin-password-field
105
- name="password"
106
- .label="${this.__effectiveI18n.form.password}"
107
- .errorMessage="${this.__effectiveI18n.errorMessage.password}"
108
- id="vaadinLoginPassword"
109
- required
110
- @keydown="${this._handleInputKeydown}"
111
- spellcheck="false"
112
- autocomplete="current-password"
113
- manual-validation
114
- >
115
- <input type="password" slot="input" @keyup="${this._handleInputKeyup}" />
116
- </vaadin-password-field>
117
- </form>
118
-
119
- <vaadin-button slot="submit" theme="primary submit" @click="${this.submit}" .disabled="${this.disabled}">
120
- ${this.__effectiveI18n.form.submit}
121
- </vaadin-button>
122
-
123
- <vaadin-button
124
- slot="forgot-password"
125
- theme="tertiary small"
126
- @click="${this._onForgotPasswordClick}"
127
- ?hidden="${this.noForgotPassword}"
128
- >
129
- ${this.__effectiveI18n.form.forgotPassword}
130
- </vaadin-button>
86
+ <div id="title" slot="form-title" part="form-title" role="heading" aria-level="${this.headingLevel}">
87
+ ${this.__effectiveI18n.form.title}
88
+ </div>
89
+ <slot name="form" slot="form"></slot>
90
+ <slot name="custom-form-area" slot="custom-form-area"></slot>
91
+ <slot name="submit" slot="submit"></slot>
92
+ <slot name="forgot-password" slot="forgot-password"></slot>
93
+ <slot name="footer" slot="footer"></slot>
131
94
  </vaadin-login-form-wrapper>
132
95
  `;
133
96
  }
@@ -69,7 +69,7 @@ export declare class LoginMixinClass {
69
69
  * and `header` sections, `header` can be added to override `title` and `description` properties
70
70
  * in `vaadin-login-overlay`):
71
71
  *
72
- * ```
72
+ * ```js
73
73
  * {
74
74
  * header: {
75
75
  * title: 'App name',