quasarframework / quasar

Quasar Framework - Build high-performance VueJS user interfaces in record time
https://quasar.dev
MIT License
25.67k stars 3.47k forks source link

[REQUEST] Responsive scaling, colour themes, and brand consistency #4320

Open digiproductmarketer opened 5 years ago

digiproductmarketer commented 5 years ago

Being relatively new to Quasar, I have no pre-existing projects on old versions, and so I'm just looking for the best possible solution for my projects as I develop them ... without worrying about breaking changes.

However, I recognise that many developers will have already pre-existing applications and may therefore be reluctant to change.

Like most developers, I envision my completed projects being used on a variety of devices ranging down from desktop computers, through tablets, to mobiles ... and obviously, I want the best possible consistent look for my projects across all these devices regardless of the screen size.

As I've started learning about Quasar, and developing my first project, I've been scouring the documentation, the forum, and the Discord server for information on how I can add responsive scaling of components, and especially typography, and also how I can portray a set of consistent brand colours thoughout the application.

I've asked questions and received many helpful responses and useful solutions, from both the team, and other Quasar users.

While doing this, I've discovered many threads at both places, discussing the need for a "themeing" feature in Quasar, so that it would be possible to pre-configure a selection of colour themes, and allow a user to choose from these.

There have also been various discussions about responsive scaling for components, and responsive typography for scalling text.

None of these items are trivial, and I see many developers struggling to to produce their own implementations of exactly the same solutions in order to handle these issues.

I understand that resources are limited, and that the Quasar Team are currently very focused on completing the V1 release, but feel that it is essential that a thorough review is undertaken of the current state of these issues within the Quasar Framework, and possible solutions for a future path to handle them.

There are a great number of highly talented developers in the forum, and in the Discord group, who I am sure would be willling to give feedback on what their exact reqirements would be, and help formulate a clear workable path towards addressing these issues.

Could we have a formalised way to discuss these issues, and provide feedback to the team so that the Quasar Team can take the best, most informed decisions and implement a suitable change to accomodate such features within the framework?

digiproductmarketer commented 5 years ago

The following issue is another issue related to theming that has just been posted ...

https://github.com/quasarframework/quasar/issues/4306

codenamezjames commented 5 years ago

Theme configuration as is

src/App.vue

...
<router-view :key="key" />
...
computed: {
    key () { return this.$store.state.settings.theme + this.$store.state.settings.dark }
  },
  created () {
    this.$store.dispatch('settings/setTheme', this.$store.state.settings.theme)
    this.$store.dispatch('settings/setDark', this.$store.state.settings.dark)
  }
...

In this file i am creating a key based on the theme + darkmode and i use that key to refresh the app on change.


src/index.template.html

...
<style>body.dark {background: #121212}</style>
...

I also like to start with a dark background. Its not as jaring to go from dark to light as it is to go form light to dark


src/store/settings/state.js

export default {
  language: 'en-us',
  dark: false,
  theme: 'default'
}

I store the default settings for the theme in this file.


src/store/settings/mutations.js

export const SET_DARK = (state, dark) => {
  Vue.prototype.$dark = dark
  state.dark = dark
}

export const SET_THEME = (state, theme) => {
  state.theme = theme
}

I set the theme state and the global dark variable in these functions.


src/store/settings/actions.js

import brands from '@/css/themes/default'
import darker from '@/css/themes/darker'
import v1 from '@/css/themes/v1'
import v2 from '@/css/themes/v2'
import v3 from '@/css/themes/v3'
const brandColors = {
  default: brands,
  v1: v1,
  v2: v2,
  v3: v3,
  darker
}

import { colors } from 'quasar'
const { setBrand } = colors

export const setDark = ({ commit }, dark) => {
  commit('SET_DARK', dark)
  if (typeof document !== 'undefined') {
    const bodyHasDark = Array.from(document.body.classList).includes('dark')
    if (dark) {
      !bodyHasDark && document.body.classList.add('dark')
    } else {
      bodyHasDark && document.body.classList.remove('dark')
    }
  }
}

export const setTheme = ({ commit }, color) => {
  if (typeof document !== 'undefined') {
    const brand = brandColors[color]

    setBrand('primary', brand.primary)
    setBrand('secondary', brand.secondary)
    setBrand('accent', brand.accent)

    commit('SET_THEME', color)
  }
}

Here is where the magic happens. I import some JSON brand files here and map them to some labels (that just happen to be the same name)
Keeping with the dark/theme convention i have 2 functions 1 that handles theme changes and the other that handles dark & light
In the setDark function i just commit SET_DARK and i check for the document to avoid ssr issues then set dark and light body classes
and for the setTheme function i check for document and then set the brand and commit the theme


src/boot/dark.js

export default ({ Vue, store }) => {
  Vue.prototype.$dark = store.state.settings.dark
}

Here i just make a global vue variable for convenience.
I use this global variable like :dark=$dark on many q-COMPONENTS all over my app

src/css/dark.styl

$dark-background = #121212
$dark-background-2 = lighten($dark-background, 5%)
$dark-background-3 = lighten($dark-background, 7%)
$dark-background-invert = lighten($dark-background, 75%)
body.dark
  background $dark-background
  color white
  .dark-background-2,
  .q-menu:not(.text-black),
  .bg-dark-2,
  .text-dark-2,
  .apexcharts-tooltip.light .apexcharts-tooltip-title,
  .q-drawer
    background $dark-background-2
  .q-popover,
  .bg-dark-3,
  .text-dark-3,
  .modal-content,
  .q-card
  .apexcharts-tooltip
    background $dark-background-3
    background $dark-background-3

  .q-card-separator
    background $dark-background-invert
  .modal-body.modal-message
    color white
  .bg-dark
    background $dark-background-3 !important
  .apexcharts-menu
    background $dark-background-3

  .apexcharts-menu-icon:hover svg
    fill white
  .apexcharts-menu-icon svg
    fill white

Last but not least the css file that handles non-component styles

Theme configuration Changes

digiproductmarketer commented 5 years ago

There's a thread on the Quasar Forum about Fluid Typography that is relevant

https://forum.quasar-framework.org/topic/3483/fluid-typography-solved

Scenario: Let’s assume you are building a typical (business/product/blog) webpage with Quasar. You want it to be flexible and responsive and VERY mobile friendly and VERY big screen friendly. After a while, you realize it is very hard to achieve with Quasar. Almost impossible out of the box. In normal scenario you would use one of the thousands Bootstrap site templates, BUT, in this case you are just stubborn to use Quasar. 🙂

One piece of the puzzle is fluid typography: https://www.madebymike.com.au/writing/fluid-type-calc-examples/ https://css-tricks.com/books/volume-i/scale-typography-screen-size/

In Quasar you could use something like this in your app.styl file:

fluid-font-size($minimumSize = 12, $maximumSize = 36, $minimumViewportWidth = 300, $maximumViewportWidth = 1600)
  font-size: 'calc(%spx + (%s - %s) * ((100vw - %spx) / (%s - %s))) !important' % ($minimumSize $maximumSize $minimumSize $minimumViewportWidth $maximumViewportWidth $minimumViewportWidth)

for num in (8..28)
  .fontsize-{num}
    fluid-font-size num (num*2+2) 300 1800

The code above will generate CSS classes fontsize-8 through fontsize-28. After that it is quite simple to use it like this:

<p class="fontsize-10"> this is content text</p>
<p class="fontsize-12"> this is caption</p>
<p class="fontsize-14"> this is header</p>
<p class="fontsize-16"> this is bigger header</p>

In similar style other CSS typography properties could be scripted (line-height etc.). I know, it is not ideal, I would strongly prefer to have something like responsive/fluid typography CSS system in Quasar itself, but for now - it just works.

in B4 there are “heading classes” and “display headings classes”. They’re quite different. In Quasar there are formally only “heading classes” but semantically and visually they are closer to B4s “display headings classes”. As for now, in Quasar, you need to decide what you want “visually/semantically” and hard code that “formally”. Which results in a little mess. And this is one of the reasons where thousands of B4 templates/snippets are very hard to convert to Quasar, because some very similar formally concepts has different semantic/visual meaning in Quasar (IMHO).

In B4 you can add something called “responsive font sizes” which is exactly what this topic is about. They’re much better than my sample/simple code of course: https://github.com/twbs/rfs

Quite frankly, having everything “fluid” could be good, but IMHO only in very specific situations. Obviously, not in “app” scenario, where is interaction and it would be awkward. Maybe in web site scenario, where you have only presentation of very simple content with many CTA and aggregated, constant info. Don’t know, wont argue, matter of taste.

Ach, “fluid” is a term coined way before “responsive”. I use term “fluid” when I think about “water” behaviour where page elements are reflowed with every little resize. With term “responsive” I tend to think about situations, where reflow depends on specific media breaks. This is the situation in Quasar. Media breaks are very opinionated, I won’t comment more (4K displays cough, cough).

What is my solution in current situation? We need to remember, that we have all the power of Vue components in our hands. We do not need to clutter our code with hundreds of divs with cryptic class names. We can distill essential elements of our websites and make components from them. And then build a page from a set of compound, beautiful, components. What said components could be? Well, for starter there could be a flex component with two columns (slots) where first column has text content and second has a responsive image (ideally from SVG). Then you can make another component, where is a title, place for paragraph and a list. And you could place this component in the left slot of the first component. And content for those component could come from component ‘data’ section. When you have a library of your own, tirelessly ciseled block element you can build your page from bricks. I have only a little objection, that my “standard web page components” somehow looks like ancient PowerPoint slides ha ha

Och, and remember you can always use inches or cm or rems as sizes in CSS - maybe what you really need is not “fluid” or “responsive” typography but a “constant everywhere” one?

outofmemoryagain commented 5 years ago

I have a similar approach to @codenamezjames but it is also IE friendly.
https://github.com/outofmemoryagain/quasar-theme-poc

Yes, it is in Typescript, but it should be easy enough to understand.

I'm not handling "dark" mode at the moment, but I'm sure a similar approach could be considered. I apply colors based on a "Theme" I'm currently using a directive and a vue plugin to extend the vue instance with a $theme property (yes, I should probably change it to $themes). This does the same as @codenamezjames does by using colors.setBrand() to set the brand color. But if IE is detected a workaround is used to apply a style to any elements that have one of the theme colors classes (ex: bg-primary, text-primary). In addition, a MutationObserver is created to watch for DOM changes to remove any theme styles and reapply them. This is done to handle classes that are applied in components due to state changes, such as QDate component when the selected day is changed.

codegrue commented 5 years ago

I ended up expanding the default variables to have more and be similar to the example in the dark themes documentation:

// Screen Elements
$primary            = #0052a3
$secondary          = #03DaC6
$accent             = #f57b00
$surface            = #EEEEEE
$body-text          = #FFFFFF
$background         = #FFFFFF

// Buttons/Feedback
$positive           = #21BA45
$negative           = #C10015
$info               = #31CCEC
$warning            = #F2C037

// Special Areas
$title-text         = #000000
$title-background   = #cce6ff
$header-text        = #FFFFFF
$header-background  = #0052a3
$card-background    = #888888
$footer-background  = #E8E8E8

Might be useful to have a larger standard set.

DmitrijOkeanij commented 5 years ago

Skin compiler.

My thoughts.

I need skin system for my Engine. I think this is common case for many projects.

What is skin? Some aditional css styles above Qausar CSS.

To build custom skin I need

  1. Some command to compile my stylus skin file. It may be extension. I dont know the extensions syntax but it can be something like this quasar compile-skin my-skin.styl And this command produce css code in one file => my-skin.css

  2. I need to apply my-skin.css to the site, so Quasar Api need to have function to apply

    import {applySkin} from "quasar"
    applySkin("MySkin", "/somedir/my-skin.css");  //  1 parameter is skin name, it needed to remove skin by name.
  3. And to remove skin removeSkin("MySkin")

  4. Ability to add many skins at one time.

    applySkin("MySkin", "/somedir/my-skin.css");
    applySkin("Skin2", "/somedir/my-skin2.css");
    applySkin("Skin3", "/somedir/my-skin3.css");

    It needed to skin different components separetly if I want.

  5. Ability to remove many skins

    removeAllSkins()
    removeSkins(["MySkin","Skin1"]);

This is my vision of how skins have to work.

codegrue commented 5 years ago

Can you do something like this in stylus?

$dark = true

themeColors(dark)
  dark == true:
    $primary            = #0052a3
    $secondary          = #03DaC6
    $accent             = #f57b00
  else:
    $primary            = #000000
    $secondary          = #222222 
    $accent             = #333333
codegrue commented 5 years ago

I agree with @Dmitrij-Polyanin that the concept of a skin is a good one that somehow packages colors and CSS together into something that can be loaded dynamically.

digiproductmarketer commented 5 years ago

Everyone's idea of what a "theme" represents will be different ... I think any proposed solution needs to recognise that ... there will never be a one-size fits all solution to this ....

And, it has to be considered that the Quasar eco-system will continue to grow .... new components will appear (either as core components or as app extensions)

So,, theme files created a month ago, would not contain any styling features for the recently released QRibbon app extension .. the necessary styling options would simply not exist in the style files How would the necessary styling for QRibbon (and any future new components) get added to the "theme" solution.

How will the necessary styling for the QCalendar App Extension be integrated into a theme solution? It's going to require many new styling options and is significantly different in layout to many other components.

What about the necessary style for the Chat Message component?

AND ... perhaps a fundamental question should be asked at this stage ....

IS IT REALLY THE RESPONSIBILITY OF QUASAR TO HANDLE THIS?

How far would such a system fulfill the real needs of developers?

Will it just handle colours?

Will it also handle fonts?

Will it handle margins and padding?

Will it handle drop-shadows?

How would it handle each of the above on a component by component level?

This is a rabbit hole that probably has no end ... and no final solution that will satisfy EVERY developer.

Do the Quasar Team even have the resouces available to devote to develop such a project?

Do they have the resources available to continue maintain so a solution as new components get added to the Quasar Framework ... either through core components or via App Extensions?

Do individual developers have the resources to maintain their own themes as they add new components to their apps? How will developers custom themes handle default values for styling new components?

How will the system handle new custom components that we developers create and want to then incorporate into our theme management system?

Perhaps much of what's needed already exists in the Quasar Framework ...

I asked a question over on the Forum about how I would add additional brand colour names ... such as ListHeaderToolbar ActionButton and received a solution for this using features already present in the Quasar Framework.

Perhaps, rather than a new solution involving changes to the Quasar Framework or new App Extensions ... all that's really necessary is a detailed tutorial explaining at a very basic and detailed level how to create new colour variables ... and group them into a set (a "theme") ...

Perhaps it goes a little further, and provides an App Extesnsion that "loads" a set of colour variables (a "theme") allows editing of them in a colour picker and re-saving back to the source "styl" file.

But again ... this will need a way of handle changes and growth to thw Quasar Frameowrk components ... and new colour variables that need to be inserted into every "theme".

Sorry this has rambled on ...

I guess what I am saying after all this is ...

Much of what is needed regarding colour management from a theme point of view is already present in the Quasar Framework ... and we probably just need a better education to developers of the existing features to allow them to implement a solution suited to their own particular application's needs ...

And ... perhaps this is a situation where a video tutorial would be more beneficial than a written tutorial ...

nothingismagick commented 5 years ago

Just wanted to let you all know that I am working on this right now and will try to have a first revision of an RFC for you this week.

digiproductmarketer commented 5 years ago

Dmitrij has just posted some thoughts of his about skinning on a separate issue ... you may wish to read that while formulate your RFC ...

https://github.com/quasarframework/quasar/issues/4354

digiproductmarketer commented 5 years ago

Perhaps the theme system in the recently released VuePress 1.0 will give some ideas on how to do themeing ...

https://medium.com/@_ulivz/intro-to-vuepress-1-x-7e2b7885f95f

DmitrijOkeanij commented 5 years ago

Now I am trying to remove Material Icons from project and use only Fontawesome. And as it is now, I need to change any icon in any place of my big small project. I am not talking about my custom icons, I am talking about system icons like chevrons on dropdowns, expansion items and others system icons like error on QInput.

[Request] - To make centralize place to controll all common ellements

То make system to customize in one central place all common ellements.

Now if I need to change chevron icon of QExpansionIcon I need to do it in all components.

And if I use some theme common style decision like to use outline style, I think I need to set it once, not on all component. Or to have easy system to apply styles on all elements, not to sell all properties manually.

[Idea, Request] Skin/Theme JavaScript applyable object

To make skin/theme object with ability to set any props: sizes, color, icons and so on. And to skin any part of conent.

<div q-skin="MySkinName">
  ... some code
</div>

And after that all conent inside div will be skinned with MySkinName.

And this object can set parameters directly to any sub components like

const skin = {
   outline: true,
   QExpansionItem: {
      expand-icon: "fas fa-angle-down"
   }
}

And to set only to some component if it has class, see onClass: "my-class" in the example

const skin = {
   outline: true,
   QExpansionItem: {
      onClass: "my-class"
      expand-icon: "fas fa-angle-down"
   }
}

It is like css extension.

I am not sure that it is easy, and not sure that it need to be make by QTeam, maby it is task to VueJs to make this possible. This is idia to start thinking about.

nothingismagick commented 5 years ago

@Dmitrij-Polyanin - you can use the method described here to change the icons of quasar components: https://quasar.dev/options/quasar-icon-sets

DmitrijOkeanij commented 5 years ago

@nothingismagick Thanks! My post not only for this thing, idea to use skins on custom areas

nothingismagick commented 5 years ago

I know.

digiproductmarketer commented 5 years ago

There has just been a post of the Quasar Forum about a package called RFS ... which stands for Responsive Font Size

I wonder if this would be worth looking into for incorporation into Quasar ... or even just to give some inspiration of how to proceed with responsive fonts for Quasar

https://github.com/twbs/rfs

therealcoder1337 commented 2 years ago

is this feature still on the radar? it would really add value to the ui, since now you cannot scale it easily. by changing the body's font-size, some texts get larger and others don't. one scenario would be where you have the same quasar app, but on different screens: monitor vs. touch screen view would require different sizes of the ui elements and fonts, which seems non-trivial with the current quasar. i guess i'd have to copy and rewrite some quasar css to make this happen now.