porsager / bss Goto Github PK
View Code? Open in Web Editor NEWπ¨ Better Style Sheets
License: Do What The F*ck You Want To Public License
π¨ Better Style Sheets
License: Do What The F*ck You Want To Public License
As noted elsewhere, apparently the ;
from background data urls are removed as demonstrated in this example.
It appears bss in cases foxdonut/meiosis-examples#9 (comment) might use excessive memory.
Something to look into....
I've been using bss for a while now, but I had no idea that I could use helpers inside of lean strings.
Based on the documentation I thought they were only accessible in the following way:
m('div' + b.f1.p1.size(200).align('center'))
Recently I discovered this is valid as well:
m('div' + b`f1;p1;size 200;align center`)
I only discovered this because @osban posted this flems on the mithril gitter.
$nest
to allow single hash input// make
b.$nest({
'td' : 'color red',
'th' : 'color blue',
})
// equivalent to
b
.$nest('td', 'color red')
.$nest('th', 'color blue')
// make
b.$nest('td, th', 'padding 1em')
// equivalent to
b.$nest('td', 'padding 1em').$nest('th', 'padding 1em')
What do you think?
I'd like to be able to run an application across several windows. At this point, the don't make me think magic style injections become a limitation.
I realise addressing this kind of outside context problem raises all sorts of other concerns - why not chuck in SSR and GTK native decorators while we're at it? - but for my part I'd be happy with what I think is a pretty modest proposal - a b.toString()
method that returns the whole sheet as a string.
I think this would do a good job of preempting meta concerns down the road (multiple instances, stateless mode, etc) without complicating the API too much.
Hello @porsager
Lines 136 to 140 in 64d8d7f
Would it make sense to automatically convert fractions to percentage values instead of adding px
?
so instead of
b({ width: 1 / 3 }) // -> { style: { width: '0.3333333333333333px' } }
you'd get
b({ width: 1 / 3 }) // -> { style: { width: '33.3333333333333333%' } }
Or can you think of a situation where %
would not be the desired outcome when working with Integer
< 0
please consider the following scenario:
I have preset styles grow
and hoverShadow
, both accessing pseudo :hover
& :focus
:
const preset = const preset = {
grow: b({
__style: {
'-moz-osx-font-smoothing': 'grayscale',
backfaceVisibility: 'hidden',
transform: 'translateZ(0)',
transition: 'transform 0.25s ease-out',
':hover': _growHover,
':focus': _growHover,
':active': { transform: 'scale(.90)' }
}
}),
hoverShadow: b({
__style: {
cursor: 'pointer',
position: 'relative',
transition: 'all 0.5s cubic-bezier(0.165, 0.84, 0.44, 1)',
'::after': {
content: '""',
boxShadow: '0px 0px 16px 2px rgba(0, 0, 0, .2)', // box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3)
borderRadius: 'inherit',
opacity: '0',
position: 'absolute',
top: '0',
left: '0',
width: '100%',
height: '100%',
zIndex: '-1',
transition: 'opacity 0.5s cubic-bezier(0.165, 0.84, 0.44, 1)'
},
':hover': { '::after': { opacity: '1' } },
':focus': { '::after': { opacity: '1' } }
}
})
}
When I attempt to combine both preset styles on a base style, e. g.
const base = b`
width: 100px
height: 100px
bc: lightgrey
margin-left: 14
margin-bottom: 28
`
... there are two options:
a) String concatenation (e. g. mithril hyperscript query)
m('div' + base + preset.grow + preset.hoverShadow, 'String composition')
b) bss helpers
b.helper(preset)
m('div' + base.grow.hoverShadow, 'Helper composition')
Now option a) with strings works as expected, of course
However, the helper variant will only shallow-merge the pseudo selectors and hence miss the hover-grow effect
here is a flems to illustrate
I assume the assign
function in lib/utils
is responsible for the merge
Lines 58 to 64 in bebc419
maybe it could be extended with something more recursive like this:
const _blnkObj = {}
/**
* mergeStyles :: (a, b) -> Object
* Immutably deep merge two objects
*
* @param a
* @param b
* @return {object}
*/
export function mergeStyles (a = _blnkObj, b = _blnkObj) {
const list = Object.keys(b || _blnkObj)
const res = Object.assign({}, a)
for (let i = 0; i < list.length; i++) {
const k = list[i]
const ak = a[k]
const bk = b[k]
res[k] = (typeof ak === 'object' && !Array.isArray(ak)) ? mergeStyles(ak, bk) : bk
}
return res
}
Eg something like
::-webkit-inner-spin-button
Would be nice to be able to do something like:
b`
background blue // god i love blue
color red // pick a better color
`
I've try to add 2 rules for same property like below:
console.log(b.cursor('ns-resize').cursor('-webkit-grab').style)
But result is only last style, is this an issue?
b`userSelect none`
This works fine in Chrome but is apparently not correctly prefixed for Firefox. Using mozUserSelect none
explicitly works as expected.
I did a bit of poking in Firefox and found that, for cssProperties
in utils.js
:
cssProperties.includes('userSelect') === false
cssProperties.includes('MozUserSelect') === false
For Chrome:
cssProperties.includes('userSelect') === true
cssProperties.includes('webkitUserSelect') === true
Not sure if any of that helps... Sorry, the code is kind of beyond me.
Think below:
b.$nest({'td':{color: 'blue'}}).class
console.log(b.getSheet())
It's not generate any class, can it be supported?
Now: border('1px solid #eee')
or border(1, 'solid #eee')
Add: border('1 solid #eee')
HEY! :D
As you may know, I dislike parentheses. Therefore this displeases me:
css.$nest(', *', `
display: flex;
flex: 1 0 0;
align-items: stretch;
`)
But what about...
css.$nest`, *``
display: flex;
flex: 1 0 0;
align-items: stretch;
`
Isn't that first line some of the tastiest punctuation soup you've ever seen? :D
Is it intentional that shorthand properties are not available when using JS objects to specify CSS rules? For example:
console.log(b('b 1').style);
console.log(b('b: 1').style);
console.log(b.b(1).style);
console.log(b({ b: 1 }).style);
Output:
{ bottom: '1px' }
{ bottom: '1px' }
{ bottom: '1px' }
{ b: '1px' }
Hi π apologies if this is intentional behaviour or if this is an existing issue (I couldn't find anything). I've run into this issue a lot recently as I've started using $nest
more and more.
I think the style sheet should just concat. I assume this is tied to bss' mechanism for preventing regenerating duplicate styles across redraws. Maybe the caching mechanism need's to key using the full definition instead of just the selector.
Duplicate selectors do not prevent non conflicting styles from being applied.
The same selector cannot be reused, even for non conflicting styles.
when has something set in b.css
using class as selector, it's very easy to override inline bss definitions.
Please see this demo
The li
never be blue
.
Is there any way to lift selector specificity?
Please close if this isn't a concern but I just came across it, and was a bit confused for a sec, might be worth documenting.
m('a' + b.d('block'), { hidden: true }, 'Still Visible')
Makes sense why it happens, and easily solvable.
Ran into this issue trying out a few helpers, and noticed that the hypen-cased helper doesn't get called since it seems the lean string gets converted to camel case whereas the helpers definitions are left verbatim.
Is there a recommended approach in bss for applying styles to the body or as top level wildcards?
Please check this code
Is this an issue?
Hello,
I am getting a strange error from UglifyJS.
Running this through webpack
Here is the error:
{ Error: 4.714f43602dc9cdd1454a.js from UglifyJs
Unexpected token: operator (>) [4.714f43602dc9cdd1454a.js:29,12]
It points to this section in the bss code:
const cssProperties = ['float'].concat(Object.keys(
findWidth(document.documentElement.style)
).filter(p => p.indexOf('-') === -1 && p !== 'length'))
which looks fine.
I don't know what might cause this. The code works fine. It just wont minify.
Line 35 in a1d6071
b({
backgroundColor: 'pink',
fontSize: 1.3 // <- no unit
})
while suprisingly seems to work fine
b({
backgroundColor: 'pink',
fontSize: '1.3px' // <- with unit
})
not really an issue, but may be helpful to remember in case you want to make it work in node down the line. Hence posting it here.
I'm make a module bss-react to make bss work better with react, it's a very early idea, the usage is only simple case:
bssReact(`color: red`, 'some', 'external', 'classes')
Or
bssReact({
backgroundColor: 'black',
textAlign: 'center',
$hover: {
backgroundColor: 'red'
}
}, 'some', 'external', 'classes')
The restriction here is it cannot chained. Any suggestion for this?
First of all thank you for awesome module!
We get output like: .component { ... }
and .component.mixed { ... }
Output: .component.component { ... }
and .mixed.mixed { ... }
To avoid wrong auto creations
I'm thinking it would be nice to put a little magic on the shorthand properties.
Currently it's a bit annoying for collisions of popular properties such as bottom / border
and padding / position
.
@thomasll came up with the idea of auto-detecting the correct property depending on the value.
This would make it possible to allow both p 10px
and p absolute
.
What do you think?
I'm thinking the shorthands are actually better off being added as helpers instead of having them automatically mapped.
I feel there's too much uncertainty with the auto registration resulting from taking all browser registrered property names and mapping them to their shorthand by initials.
I think I'd rather specify them statically in a named export that can be added using .helper()
.
It would look something like this:
import b, { shorthands } from 'bss'
b.helper(shorthands)
Another one that I'd add in a simliar fashion would be unique values as property names. That would look like this:
import b, { shorthands, valueProps } from 'bss'
b.helper({
...shorthands,
...valueProps
})
// yay
b`
absolute
hidden
scale(2)
`
Hello @porsager
just stumbled over this:
in the README.md
under 'Ways of writing CSS', it says:
JS Objects
b({ backgroundColor: 'black', textAlign: 'center', $hover: { backgroundColor: 'red' } })
while the only way I could make it work, was actually a POJO / function mix like this:
b({
margin: '2em',
backgroundColor: 'black',
textAlign: 'center',
}).$hover({ backgroundColor: 'pink '})
I am wondering if this is just a typo in the README.md
or if it was actually supposed to work the POJO way, too?
The POJO way of writing css may be advantageous when it comes to media queries:
I am currently working on a little script to provide React-agnostic styled-components
and styled-system
functionality with lovely bss
doing the heavy lifting under the hood. Consider the following output from a styled-system
type of function such as space
where this
// (attrs :: Object, theme :: Object?) -> Object
function space (attrs, theme = {}) { /* sausage factory */ }
let spacing = space({
p: '1px 2px 3px 4px',
mr: [0, 2, 4, 6],
ml: [1, 3, 5, 7],
mb: [0, 2, 4, 6],
mt: [1, 3, 5, 7]
})
...yields an Object
like this (after some tweaking of the original space function):
{
padding: '1px 2px 3px 4px',
marginTop: '4px'
marginLeft: '4px',
marginRight: '0px'
marginBottom: '0px',
$media: {
'screen and (min-width: 40em)': {
marginBottom: '8px',
marginLeft: '16px',
marginRight: '8px',
marginTop: '16px'
},
'screen and (min-width: 52em)': {
marginBottom: '32px',
marginLeft: '64px',
marginRight: '32px',
marginTop: '64px'
},
'screen and (min-width: 64em)': {
marginBottom: '128px',
marginLeft: '256px',
marginRight: '128px',
marginTop: '256px'
}
}
}
if 'Ways of writing CSS' worked as currently documented, I would be able to just drop the space
yield into bss
as is, right?
const result = bss(spacing)
as opposed to iteratively calling result.$media(ding, dong)
like this:
const { $media } = Object.assign({}, spacing)
delete spacing.$media
let result = bss(spacing)
for (let k in $media) {
result = result.$media(k,$media[k])
}
... which works just fine, but seems more wasteful. But maybe it isn't under the hood. What do you think?
Like b.d('flex'), I had to put it explicitly b.display('flex') to make it work. Not sure if it is happening to another abbreviations.
Edit: Corrected grammar
The BSS parser eagerly presumes end of line to mean end of property value declaration, but this isn't the case in CSS proper.
G'day, love BSS - keep up the good work π
I'm trying to use a unitless value for line-height (MDN) however bss is adding 'px'.
For example, when I pass in 1.25 as an int or the string '1.25', I am getting 1.25px however I want 1.25 (uses the number multiplied by the element's font size).
I just spent an afternoon tracking down an elusive bug to the following code
<span className={b.fw(600).c('rgba(0, 0, 0, 0.7').class}>{children}</span>
When rules are added with sheet.insertRule, the only effect is the invalid color rule not being applied.
When in debug mode (which was turned on in an unrelated part of the codebase), all subsequent rules are inside the unclosed color rule, which breaks everything except the component containing the bug.
It's not work when using $nest
with other pseudo selectors, like below:
b
.d('block')
.$nest(
'button'
, b.c('red').$hover(b.c('orange'))
)
Is it an issue?
I just noticed that the detection trick would fail for the flex
shortcut that can take both plain numbers (for flex-grow
) or lengths (for flex-basis
).
A user that specifies flex: 2
would expect it to mean flex-grow: 2
, not flex-basis: 2px
...
There may be other cases where the cache needs to be pre-populated, not sure which ones.
Hi, @porsager I just notice this thanks to @JAForbes' tweet.
Would you mind adopting the j2c backend API? The general idea is that, rather than having your functions return strings, you pass a backend that accumulates them. So for example
export function selectorBlock(selector, style) {
return selector + '{'
+ stylesToCss((typeof style === 'string' ? stringToObject(style) : style))
+ '}'
}
would become
export function selectorBlock(selector, style, backend) {
backend.rule(selector)
stylesToCss((typeof style === 'string' ? stringToObject(style) : style))
backend._rule()
}
and
function propToString(style, k) {
return (vendorRegex.test(k) ? '-' : '')
+ camelCaseToHyphen(k) + ':' + style[k] + ';'
}
would become
function emitDecl(style, k, backend) {
backend.decl((vendorRegex.test(k) ? '-' : '') + camelCaseToHyphen(k), style[k])
}
The main advantage here is that it allows one to write plugins that decorate the backend, like this prefixing plugin that no one uses currently because I've yet to publish j2c v1 that uses the same architecture.
The prefix plugin supports more than one prefix per browser (FF and Edge have some properties that are exclusively available with a webkit prefix), is IIRC 3KB mingzipped and uses feature detection rather than a list of properties/selectors, etc... like autoprefixer and the inline style prefixer.
The architecture makes it trivial to turn one rule into many (e.g. flex-direction
=> -webkit-box-direction
+ -webkit-box-orient
) without causing the allocation of extra objects.
Edit I can't English tonight...
Edit2: here's the full feature list: https://github.com/j2css/j2c/tree/3039875258bd69f04a16c51b69ca71361b5d5560/plugins/prefix-browser#features
In Firefox,
sheet = b({appearance: "none"})
sheet2 = b("appearance: none")
sheet + sheet2
p(sheet.getSheet())
p(sheet2.getSheet())
.bajq6bd1.bajq6bd1{--moz-appearance:none;}.bajq6bd2.bajq6bd2{appearance:none;}
''
--moz-appearance
should be -moz-appearance
, as should appearance
, I suppose.
Also, are you intentionally duplicating the classes? It has an impact on the specificity of the selector, but is probably harmless otherwise.
At last, AFAICT getSheet()
is not documented, but it is supposed to behave like that?
There's a little inconsistency with letter-spacing
and auto-px since letter-spacing
allows floats as a unit. Bss can't guess when the intention is to have px auto added or if a fraction was actually desired.
A current workaround is to do letter-spacing: calc(2)
but I'd like a better solution for v2, either just exclude the letter-spacing
property from the auto-px list, or some other more clever thing :P ?
There's something I really dislike about assigning BSS as style and class (and .toString()
TBH) at the trailing end of a multi-line, multi-block chain. Having that stuff visible ahead of time would be good.
What do you think about introducing simple b.class
and b.style
functions that simply consume an object (or operate as tagged template interfaces like b
) and return the corresponding property of the same name?
β¦the same way BSS currently does for class names. That way we can avoid accidental cascades!
Hi @porsager π
I'm not sure if this a bug or just me misusing the library π.
It seems $nest
doesn't apply within a $media
if there is already a non-empty declaration. See the repro below:
If the $nest
is chained without a parent declaration, it works fine:
I am not sure if the generated syntax is correct. But the outputted getSheet
shows a comma separated css block. But I think if it's not comma separated, it works fine.
Generated:
@media (min-width: 0px){
.b69m3411.b69m3411{
font-family:Helvetica;
}
,.b69m3411 h1{
color:red;
}
}
Proposed generation:
@media (min-width: 0px){
.b69m3411.b69m3411{
font-family:Helvetica;
}
.b69m3411 h1{
color:red;
}
}
I hope that's helpful! If I'm doing anything strange please let me know!
Eg. the following @font-face declaration fails
b.css({
'@font-face': b(`
font-family: 'Titillium Web';
font-weight: 200;
font-style: normal;
src: url('/fonts/Titillium-Web-200.eot');
src: url('/fonts/Titillium-Web-200.eot?#iefix') format('embedded-opentype'),local('Titillium WebThin'),local('Titillium-Web-200'),url('/fonts/Titillium-Web-200.woff2') format('woff2'),url('/fonts/Titillium-Web-200.woff') format('woff'),url('/fonts/Titillium-Web-200.ttf') format('truetype'),url('/fonts/Titillium-Web-200.svg#TitilliumWeb') format('svg');
`)
})
This is something I talked to @fuzetsu about in the Mithril.js Gitter.
I'm coming from using exclusively plain .css files and atomic CSS frameworks like Basscss. Usually, when I'd build a component (in my case, a Mithril component), I just chain a bunch of Basscss classes to style it. bss is great because I'm able to define additional component-specific styles very easily, and not have to go to a separate .css file to define 5 different atomic classes I'd probably only use once.
Here is how I'm using bss at the moment in conjunction with Basscss:
const bssClass = b`
transition all 0.2s ease
`.$hover`
background-color red
`;
const StyledBtn = `button.btn.btn-primary.not-rounded.${bssClass}`;
const Btn = {
view: ({ attrs, children }) => m(StyledBtn, attrs, children)
};
While this works totally fine for my purposes, I think it would be nice to have a built-in way to specify classes to compose, perhaps something like this:
const StyledBtn = b`
$compose btn btn-primary not-rounded
transition all 0.2s ease
`.$hover`
background green
`;
Which may produce a className string such as
.btn.btn-primary.not-rounded.bdp4f3o1
for me to use with my component of choice.
What do you think?
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.