thinkuniform / uniformcss Goto Github PK
View Code? Open in Web Editor NEWA fully configurable utility class generator and CSS framework built for Sass projects.
Home Page: https://uniformcss.com
License: MIT License
A fully configurable utility class generator and CSS framework built for Sass projects.
Home Page: https://uniformcss.com
License: MIT License
The top left corner of the project page shows 1.2.6, but this repository has 1.5.2.
To be clear: the framework will allow me to do exactly what I want to do. Everything that follows is just about improving the out-of-box experience.
===
It's taken me some time to work out a good responsive sizing strategy in UniformCSS. I think there are three main issues:
Some things are "t-shirt" sized; others are not.
The t-shirt sized things don't scale responsively the way you might expect.
The not-t-shirt sized things are defined in terms of rem
s (e.g., 0.625rem
), but labelled in terms of pixels (e.g. size-10
).
The first issue means that it takes some time and effort to work out a correspondence between the two kinds of scales — especially at different breakpoints. Not at all unusual, but perhaps unnecessary friction.
The second issue is easier to illustrate with an example.
To begin with, I wanted 18px font sizes on phones and 20px font sizes on desktops. Here's one way to get that:
body {
@include apply("text-lg sm.text-xl");
}
And then, looking at the scales, I decided that I wanted a 7xl h1
size on desktops, and "something smaller" on phones. So:
h1 {
@include apply("text-6xl sm.text-7xl");
}
That seems like it works. If you do the math, though, you'll see that I haven't just changed the size, I've also changed the ratio:
text-lg
= 18px, text-6xl
= 40px, 18:40 = 1:2.2text-xl
= 20px, text-7xl
= 48px, 20:48 = 1:2.4Not fatal, but not intended either. More importantly, if I'm trying to come up with a general solution to responsive sizing, I better not take that approach to re-sizing space, where changing proportions are more likely to be visible. (Think about something that has a 2:1 horizontal-to-vertical padding ratio — and then doesn't.)
What I would generally do — though not all CSS frameworks are very happy about this — is to base everything in rem
s, and scale my root font size at different breakpoints. Obviously that simplifies almost everything, e.g.:
h1 {
@include apply("text-6xl");
}
body {
@include apply("text-md");
}
and pushes responsive re-sizing into html
. (It can also introduce new problems, but for the kinds of work I do these days it's usually a better starting point; there's less tweaking to do.)
The reason I didn't do that right away was the third issue. :-) I was already expending mental effort because the non-t-shirt sizes (e.g., size-10
) were defined in terms of rem
s, and those rem
s were different from my body
font sizes on phone and desktop. (That's an issue because, for example, I'd like to add rem
-based bottom margins to h1
, p
, etc.) But even worse: they were labelled in pixels even though they were defined in rem
s. Thinking about all of those things at the same time exceeded my mental load limit!
===
Here are a few general suggestions:
rem
s as rem
sTo be clear: I'm not saying that all of those things must be done for any of this to be usable. Rather, I'm saying that there should be one cohesive story out of the box. If there's choice, all the better. But at least one.
Btw, it's very possible that you had a model in mind that just didn't occur to me (or I missed it in the docs). If so, please let me know!
===
As I said, everything I want to do I can. All of this is just about the out-of-box experience.
Here's what I'm going to try today:
sm
)I'll see how that goes.
Describe the bug
I have a set of classes: flex flex-col sm:flex-row sm:gutter-x-16 gutter-y-8 sm:gutter-y-0
But the margin-reset in the gutter-y-0 stops gutter-x-16, so there is no space between elements above sm. It works if only margin-bottom and margin-top are reseted.
Thank you for this library, it is awesome!
Norbert
This is a small convenience thing, but it would be nice if:
.some-class {
@include apply("");
}
did nothing rather than producing an error.
(In the process I'll describe later on today, I added classes to my HTML, and then added the classes to my CSS, and then started styling the classes. When I added the classes I added empty apply
s — but apparently you can't do that today. As I said: just a small convenience thing.)
Is your feature request related to a problem? Please describe.
Recently, I attempted to swap out the default ratio
that uses padding for a custom ratio
utility that uses aspect-ratio
instead. It was a bit complex to do since ratio is a special utility.
Describe the solution you'd like
It depends, I am curious how often people prefer using the padding approach versus the new native aspect-ratio
approach to do this sort of stuff?
If keeping the padding approach, I would suggest making ratio
overridable in the theme settings.
If the padding approach was implemented prior to css aspect-ratio
and no longer is relevant - I would recommend replacing padding/height with the new aspect-ratio
property.
Additional context
Currently, I am using aspect-ratio to uniformly and exactly size various images and videos on a blog. Both methods work, aspect-ratio
is just a lot easier to work with in varying layouts.
Is your feature request related to a problem? Please describe.
When using the a gutter utility class like gutter-y-16
to apply spacing between child items, there are time when overriding the spacing of a single element is necessary. Of course there's always the option to remove .gutter-y-16
and instead apply custom margins to all the child elements — this can be annoying though when all but one are the same value. It'd be really nice if there was a way to use a margin utility like mt-24
to override the margin on the one outlier in a group.
The problem today is that because of specificity, the .gutter-y-16 > * + *
selector will always win over .mt-24
.
Describe the solution you'd like
One option to address this situation could be to allow making some utilities use !important
while allowing most to still go without it. For example, if all the margin utilities could be set to important: true
that would allow me to override gutter margins for exceptions as they come up. Additionally, due to the nature of margin utilities there isn't really much risk in using !important
as they're highly unlikely to collide in any unexpected ways.
Describe alternatives you've considered
Alternatives in my mind include:
gap
+ flex
/flex-column
utilities instead of gutter
. With flex + gap you can use margin utilities to add extra space or negative margin utilities to cancel out gaps as needed. I think this is the best alternative, though technically browser support isn't quite fully there yet. Additionally, applying flex
does impact the width of child elements unless you add extra classes.gutter
and manually space all the individual items!important
without doing it for allIs your feature request related to a problem? Please describe.
So sometimes you have a radio set in a form that you want to treat more visually, rather than with plain radio buttons. A common CSS pattern for this situation is to wrap the radio inputs with label
elements — so they can still be check via clicking — and then to use a :checked +
or :checked ~
sibling selector to style the more visual representation when its accompanying radio is checked. Here's an example:
Describe the solution you'd like
Similar in spirit to the group-hover
pseudos, it'd be neat to be able to have some sort of checked-sibling
pseudo that you could use for situations like this. For that example above, I could imagine using it to apply the blue border when the sibling radio was checked. Something like:
<img src="..." class="shadow-sm border-2 border-transparent checked-sibling.border-blue" />
And the resulting CSS would look like:
:checked ~ .checked-sibling\.border-blue {
--border-opacity: 1;
border-color: rgba(var(--blue), var(--border-opacity));
}
Describe alternatives you've considered
I haven't been able to come up with any alternatives to this kind of pseudo that don't require me to either write some javascript behavior or some custom CSS. I'll probably end up implementing this with visible radios for now since I'm trying to go as far as possible without custom CSS.
Describe the bug
Looks like a typo snuck in by pluralizing list-style-position
to list-style-positions
. You can see the property name on MDN here is list-style-position
. But in /utilities/typography/core-list-style-position.scss
it uses a pluralized form: list-style-position
.
Ultimately this results in browsers not rendering the style, because the property is unrecognized.
To Reproduce
list:outside
to an elementSidenote: @jinsupark for something like this would you prefer that I open a pull request or something instead? I haven't contributed to a project like this before, so it'd be helpful to understand your preferred process.
Consider generating auto
variants where appropriate — e.g., mx-auto
, for old-style centering.
To be clear, it's already easy to get the generator to produce them, e.g.:
@use "uniform" as * with (
$config: (
utility: (
margin-x: (
extend: (
variants: (
auto: auto
)
),
),
),
)
);
It might just be a better out-of-the-box experience if they're already there.
(I ran into this as soon as I started trying to markup an existing page.)
When I first saw what your framework was capable of, this is what I immediately thought of: that I could use it to very efficiently write traditional CSS — like the examples on your Extracting Components and Helper Mixins pages:
.parent {
@include apply('p-40 shadow-2xs radius-2xl');
&__child {
@include apply('hover.opacity-50 p-24 md.p-64');
}
}
That is remarkably concise. And maintainable. Combine it with your headless
mode, and — holy cow.
That's something special.
(Maybe Tailwind can be used that way, but if it can, I missed it. Well — I know it can almost be used that way. I just don't remember seeing examples as terse and complex as yours.)
As an extra bonus, I even discovered that I could still "include" reset
and standard
with headless
output, e.g.:
@use "uniform" as * with (
$config: (
output: headless,
includes: (
reset,
standard
),
),
);
So with UniformCSS I can be far more productive; I can produce only the CSS I want; and all I need is a SASS compiler.
🤯
At the moment you document this, but in pieces; I think headless
is only mentioned on the build settings page, and apply
on the two pages I gave above.
I think you should consider pulling the all of those pieces together in one spot, to tell this story. Because this feels like something I haven't really seen before.
I should say: I'm mostly a mobile developer rather than a web developer. But when I do web development I tend to start with a CSS framework. No matter how good the framework is, I always end up having to eventually work around hard-coded assumptions. It seems like your framework — library? — might give me another option.
It's almost like a CSS framework construction kit.
I couldn't figure out how to set this up with Vite via npm after I installed it. Vite couldn't find the scss file to import.
So, instead, I ended up cloning the repo, thinking this would work:
// main.scss
@use 'uniformcss/uniform' as *;
This resulted in errors.
Error: Can't find stylesheet to import.
╷
1 │ @forward "uniform/core";
Finally, I just moved uniform directory out of uniformcss directory.
This worked, but it's not ideal.
// main.scss
@use 'uniform' as *;
Describe the bug
When I use this example for generating custom properties I get a compilation error:
Error: $map1: null is not a map.
╷
361 │ $compiled-properties: map.merge($static-properties, $merged-properties);
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
╵
_index.scss 361:27 tree-constructor()
_index.scss 549:1 @use
src/sass/style.scss 47:1 root stylesheet
To Reproduce
Steps to reproduce the behavior:
$config
map within utitilities
mapExpected behavior
Expected compiling to just work and the new utility class to be available.
Additional context
Maybe I've formatted something incorrectly, so just in case, here is my .scss
file contents:
$config: (
delimiter: "\\:",
pseudo-delimiter: ":",
screen-delimiter: ":",
utilities: (
background-color: (
pseudos: (hover, active, focus, focus-within, group-hover)
),
border-color: (
pseudos: (hover, active, focus, focus-within, group-hover)
),
text-edge: (
shorthand: text,
properties: (text-edge),
variants: (
cap: cap alphabetic
)
)
)
)
);
@include apply("")
$utilities: () !default;
$separator: \:;
$xs: "xs" !default;
$breakpoints: (
$xs: 0,
sm: 480px,
md: 720px,
lg: 960px,
xl: 1200px,
) !default;
@function is-int($value) {
@return type-of($value) == "number";
}
@each $mapName, $map in $utilities {
$important: map-get($map, "important");
$property: map-get($map, "property");
$values: map-get($map, "values");
$state: map-get($map, "state");
$class: map-get($map, "class");
$media: map-get($map, "media");
$range: map-get($map, "range");
$dash: "-";
@if not $class {
$dash: "";
}
$importantValue: "";
@if $important {
$importantValue: "!important";
}
$stateVal: "";
$newState: "";
@if $state {
$stateVal: ":" + $state;
$newState: $state + $separator;
}
@if $media {
@each $break, $point in $breakpoints {
@media screen and (min-width: #{$point}) {
@each $name, $value in $values {
$util-id: $break + $separator + $newState + $class + $dash + $name + $stateVal;
@if ($break == null or $break == "" or $break == $xs) {
$util-id: $newState + $class + $dash + $name + $stateVal;
}
.#{$util-id} {
@each $props in $property {
@each $start, $end in $range {
@if (not is-int($name) or $name >= $start and $name <= $end) {
#{$props}: #{$value} #{$importantValue};
}
}
}
}
}
}
}
} @else {
@each $name, $value in $values {
$util-id: $newState + $class + $dash + $name + $stateVal;
.#{$util-id} {
@each $props in $property {
@each $start, $end in $range {
@if (not is-int($name) or $name >= $start and $name <= $end) {
#{$props}: #{$value} #{$importantValue};
}
}
}
}
}
}
}
$utilities: map-merge(
(
"opacity": (
class: "o",
property: opacity,
range: (10:100),
important: false,
values: (
10: 0.1,
20: 0.2,
30: 0.3,
40: 0.4,
50: 0.5,
60: 0.6,
70: 0.7,
80: 0.8,
90: 0.9,
100: 1,
),
),
"border-radius": (
class: "br",
property: border-radius,
important: false,
values: (
"none": 0,
"xs": 2px,
"sm": 5px,
"md": 10px,
"lg": 20px,
"full": 50%,
),
),
"display": (
property: display,
media: false,
important: false,
values: (
"none": none,
"block": block,
"inline": inline,
"inline-block": inline-block,
"inline-flex": inline-flex,
"inline-grid": inline-grid,
),
),
"overflow": (
class: "overflow",
property: overflow,
important: false,
values: (
"visible": visible,
"hidden": hidden,
"scroll": scroll,
),
),
),
$utilities
);
In the grid-template-column
documentation the classes are missing an "s" at the end of them.
Current: grid-col-12
Corrected: grid-cols-12
To Reproduce
Steps to reproduce the behavior:
Expected behavior
As mentioned above, I believe this should be grid-cols-xx
Desktop (please complete the following information):
Additional context
It is correct in the 'Basic Grid Usage' section.
Describe the bug
On the API reference, there is a broken link, it makes it difficult to search for that particular property.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
On search, it's supposed to take me to the "top" section of the API reference, or on-click of the "top" link it should display the details, but it doesn't
I'm not seeing a way to opt out of Uniform's semantic colors like "success", "info", etc. And there also doesn't seem to be a way to override the default values within the config file. I would expect to be able to exclude these altogether via theme config, and likewise to be able to extend them as I do with all the other colors — though I personally only need the exclusion right now.
Without this, I'm left to write custom CSS to override the custom properties, which is less than ideal as I want to keep as much of this as possible within generated CSS via config. Additionally, when we can't exclude these it means they're present in our generated CSS and JSON (which we use for documentation), which isn't good since in our project we'd prefer to just use literal color names like green
instead of success
, so really we just don't want them present at all.
What do you think @jinsupark, possible to add a config option for excluding the semantic color utilities?
This:
pre, code {
font-family: var(--font-mono);
}
is here. But if css-variables
is configured to false
CSS vars aren't generated.
Describe the bug
The .gutter-x-*
and .gutter-y-*
utility class names imply that they'll only affect either vertical or horizontal spacing between elements. Unfortunately, their output is set up to apply margin on all sides, setting a value of 0
on the sides it's not directly setting:
.gutter-y-1 > * + * {
--gutter-top: 1;
--gutter-bottom: 0;
margin: calc(0.0625rem * var(--gutter-top)) 0 calc(0.0625rem * var(--gutter-bottom)) 0;
}
This means that any direct children of a gutter
with additional margin utilities for the other sides will not take effect.
To Reproduce
Steps to reproduce the behavior:
.gutter-y-16
to a parent element with 2+ direct children.mx-16
to one of the direct children.mx-16
margin is overridden by the overly broad margin
declaration on gutter
Expected behavior
I would expect that .gutter-y-*
would output only margin-top
and margin-bottom
. And likewise that x
would only output margin-left
and margin-right
. Something more like this:
.gutter-y-1 > * + * {
--gutter-top: 1;
--gutter-bottom: 0;
margin-top: calc(0.0625rem * var(--gutter-top));
margin-bottom: calc(0.0625rem * var(--gutter-bottom));
}
How close this frameworks gets to tailwind? For example, i can't see both flex "grids" and grids in the docs.
Describe the bug
There are a couple typos on the letter-spacing / tracking docs.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
Correct labeling:
tracking-tightest
tracking-tighter
tracking-tight
Desktop (please complete the following information):
macOS
chrome
Version 99.0.4844.83 (Official Build) (arm64)
When using output: "json"
, the resulting output is invalid JSON due to wrapping comments like so:
/*! UniformCSS v1.3.0 | MIT License | github.com/ThinkUniform/uniformcss */
/*!
{ ... }
*/
Expected behavior
I would expect raw JSON to be output, that doesn't require me to manually remove wrapping comments.
Additional context
I've setup Uniform with my application using a config/_index.scss file that does something along these lines:
$output-mode: "css" !default;
@use "uniform" as * with (
$config: (
// Build settings - https://uniformcss.com/docs/build-settings/
delimiter: "\\:",
pseudo-delimiter: ":",
screen-delimiter: ":",
output: $output-mode,
...
)
)
Then I have two separate stylesheets that I use, one (application.sass.scss) generates the CSS for my app:
// Generated utilities - configs + generation
@use "app/config/index";
@use "app/custom/button";
...
And the other (reference.sass.scss) generates the JSON for our internal CSS reference docs:
@use "app/config/index" with ( $output-mode: 'json' );
My build setup for reference.sass.scss drops the output in a css-utilities.json
file that we consume to automatically generate documentation. But...with the current issue described up top, a manual intervention is required to remove the wrapping comments from the JSON output. What do you think of removing the wrapping comments @jinsupark?
Awesome project! :) Would love to see an option to enable max-width
media queries for desktop-first design.
Is your feature request related to a problem? Please describe.
The project documentation is nice and helpful! But...when you use custom delimiters for your project, it can make using the docs challenging since the class values and such don't match what you're using in your own codebase. This is especially true for folks that are new to the system.
Describe the solution you'd like
It'd be super helpful if there was a way to enter your custom delimiters into the documentation site, similar to choosing a language. After entering those, the docs could show the appropriate delimiters across examples and such — it'd be really helpful. The same problem exists for other configurable aspects like colors, spacing scales, etc. but it seems like the basic syntax of the selectors is probably the area that might be most impactful.
Describe alternatives you've considered
Other alternatives I've considered, is making some way to auto-generate documentation within a project, based on the project's configuration. Like what if there was autocomplete available that mapped to your configuration of the system.
reset
sets text-rendering
to optimizeSpeed
here. I suggest not setting it to anything anywhere.
First, I'm not sure that it really qualifies as a "reset" kind of setting at all; it's not quite the same as different browsers using different font families or font weights or margins for the same element.
Second, even if it does, I'm not sure that optimizeSpeed
is the right choice. From Mozilla:
The browser emphasizes rendering speed over legibility and geometric precision when drawing text. It disables kerning and ligatures.
"Disabling kerning and ligatures" isn't, I think, something a reset should do.
The problem is that optimizeLegibility
has different trade-offs, and isn't right either; I don't know how things stand today, but there was a time when it had (or could have) a significant impact on the rendering time of text-heavy pages.
If anything, auto
is probably the best option. My understanding is that, with auto
, browsers make different choices at different font sizes. But auto
is the default, so there's no point in setting it to that!
(For what it's worth, Bootstrap doesn't set text-rendering
anywhere.)
I was wondering if you're able to add the feature to be able to use something like 2xl on the screens config without the output throwing an interpolation error:
screens: (
sm: 768px,
md: 1024px,
lg: 1280px,
xl: 1536px,
2xl: "screen size",
),
Having it output as .\2xl or [class="2xl"] would be amazing.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.