Skip to content

[css-properties-values-api] enhance @property with local persistence #9585

@brandonmcconnell

Description

@brandonmcconnell

Abstract

Developers often need to ensure that user-selected styles are consistent across visits and tabs, such as theme settings. To streamline this process, I propose an extension to the existing @property rule with a new persist descriptor. This addition will empower CSS to persist user-defined styles without relying on JavaScript, enabling a seamless and more native experience for managing user preferences.

1. Introduction

Persistent user preferences are a staple in the realm of web personalization. The vision is to provide a frictionless experience where users' style preferences are remembered effortlessly. Currently, this is handled via JavaScript, which can be less efficient and more complex. By extending the @property rule with local persistence capabilities, we can leverage CSS to elegantly solve this challenge.

2. @property rule enhancement

2.1 Declaring a locally persistent property

  • The @property rule is extended with a persist descriptor that determines whether the property should be read from and written to localstyles:
@property --theme-color {
  syntax: '<color>';
  inherits: false;
  initial-value: #00FF00;
  persist: true;
}

2.2 Utilizing locally persistent properties

  • Locally persistent properties can be used just like any other custom property:
body {
  background-color: var(--theme-color);
}
  • If a locally persistent property isn’t set, it falls back to its initial value or another specified value:
body {
  background-color: var(--theme-color, #000000); /* Fallback to black if not set */
}

2.3 Resetting locally persistent properties

Resetting a locally persistent property to its initial value is as simple as setting its value to initial, which will cause it to default to the initial-value set in its @property definition if there is one.

--theme-color:  initial;

3. JavaScript API interaction

3.1 Accessing locally persistent properties

// would use either `document.localStyles` or `window.localStyles`

const themeColor = document.localStyles.getPropertyValue('--theme-color');

3.2 Modifying locally persistent properties

document.localStyles.setProperty('--theme-color', '#00FF00');

3.3 Removing locally persistent properties

document.localStyles.removeProperty('--theme-color');

4. Use cases

Extending the @property rule with local persistence would serve the following scenarios and likely countless others:

  • Maintaining theme preferences across sessions and tabs without server-side storage.
  • Saving accessibility settings like preferred text size or contrast modes for user convenience.

5. Gotchas/considerations

5.1 Third-party style isolation

A key consideration is ensuring that localstyles are protected from third-party manipulation. It's essential to implement mechanisms where these styles can be designated as private, potentially through a flag or a specific scope, to prevent external CSS from altering these user-specific preferences.

5.2 Privacy & security

It's crucial that localstyles respect user privacy and are safeguarded against CSS-based security vulnerabilities. Similar to the existing web storage APIs, localstyles would adhere to privacy settings and the same-origin policy.

6. Conclusion

By enhancing the @property rule with a persist descriptor, we can unlock a powerful, native way to persist user-defined styles. This change promotes a more efficient, consistent, and user-centric web experience.

I intentionally chose to use the descriptor persist and not local, so as not to confuse local with any meaning in holds in terms of global/local scope. An unrelated but similar PR I opened a while back re global "scope" can be found here: #7866

7. Request for comments

Feedback is sought on:

  • The proposed syntax and functionality for the extended @property rule.
  • Strategies to ensure the privacy of localstyles.
  • Potential challenges and solutions for browser vendors in implementing this feature.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions