Giter VIP home page Giter VIP logo

Comments (37)

patrickml avatar patrickml commented on August 15, 2024 20

has there been any progress for this? I am trying to select td's in a table table tr td:first-of-type

from aphrodite.

zgotsch avatar zgotsch commented on August 15, 2024 9

One hack which we're cautiously using right now works only because of the way pseudo-selectors are handled.

const styles = StyleSheet.create({
  parent: {
    ':hover .child': {
      backgroundColor: blue
    }
  }
});

Which is totally not part of the API, and I fully expect it to break at some point.

from aphrodite.

misterfresh avatar misterfresh commented on August 15, 2024 3

const styles = StyleSheet.create({ parent: { ':hover .child': { backgroundColor: blue } } });

This is actually rather intuitive. I'm using it in a project.

from aphrodite.

ide avatar ide commented on August 15, 2024 2

Another motivation for supporting child selectors (outside of pseudo-classes even) is to style React components that we don't control. For example, React Bootstrap has a NavItem component that renders <li><a /></li>, and supports setting only the <li> element's className.

In this case I want to write:

StyleSheet.create({
  navItem: {
    'selector(a)': { color: linkColor }
  }
});

but sometimes would want more nested selectors like selector(a img) too.

from aphrodite.

jlfwong avatar jlfwong commented on August 15, 2024 1

Interesting! That definitely was not an intention of the API design, but perhaps is something we should support for the sake of API simplicity. I don't see any immediate reason for that to break on merging.

from aphrodite.

jlfwong avatar jlfwong commented on August 15, 2024 1

Thinking more about the selector(a) syntax, I'm worried about the non-determinism demonstrated in the following situation:

const Component = () => <div className={css(styles.foo)}>
    <div className={css(styles.bar)}>
        <span>Hello</span>
    </div>
</div>

const styles = StyleSheet.create({
    foo: { 'selector(span)': { color: 'red' }},
    bar: { 'selector(span)': { color: 'green' }}
});

The color of the resulting span will depend on the injection order of foo and bar, which is a kind of non-determinism we specifically try to avoid in aphrodite.

To see why this happens, consider the following:

<div><span><em>Hello</em></span></div>

with CSS

span em { color: green; }
div em { color: red; }

In this case, "Hello" will be red. The selectors have equivalent precedence, so the latter is used. If the CSS is switched to:

div em { color: green; }
span em { color: red; }

Then "Hello" becomes green. This reversal of the declaration order mimics what would happen in aphrodite depending on whether css is first called with styles.foo as an argument, or first called with styles.bar as an argument.

I don't think there's a clear implementation of the proposed selector(a) syntax that doesn't have this problem.

from aphrodite.

jlfwong avatar jlfwong commented on August 15, 2024

In general, having parents affect the styles of their children is a pattern we want to avoid, but I can see why this particular example might occur within a single component's markup.

I'm reluctant to see patterns like this become first class citizens, but we do need some escape hatch way of writing arbitrary CSS (whether it's globally scoped or scoped to the generated selector), akin to dangerouslySetInnerHTML, which you'd be able to use to do that.

from aphrodite.

zgotsch avatar zgotsch commented on August 15, 2024

Come to think of it, this also might break on merge

from aphrodite.

kentcdodds avatar kentcdodds commented on August 15, 2024

I also just ran into this case. I'll proceed with @zgotsch's workaround. Is there anything specific that we could do to make this a first-class citizen so I could rely on it and not worry about this breaking in future releases? I'm happy to contribute to the project with a little direction :-)

from aphrodite.

kentcdodds avatar kentcdodds commented on August 15, 2024

Actually, thinking about this more, I think that this is a scenario that would be better to avoid supporting and we should find another way to work around this issue. Here's the kind of API I'd like to see (and maybe help build):

const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      nonCssPropertyShouldCreateAnotherClass: {
        color: 'black',
      },
    },
  },
})

const { parentClassName, nonCssPropertyShouldCreateAnotherClassClassName } = cssWithDecendents(styles.parent)
// parentClassName === 'parent-blahblah'
// nonCssPropertyShouldCreateAnotherClassClassName === 'nonCssPropertyShouldCreateAnotherClass-blahblah'

Which would produce classes with the following CSS:

.parent-blahblah:hover {
  background-color: 'white';
}

.parent-blahblah:hover .nonCssPropertyShouldCreateAnotherClass {
  color: 'black';
}

Obviously, the names are up for debate. But I think an entirely new method should be used and we shouldn't overload the css method. Also, the css method should probably warn when given a property that is not a css property it understands (maybe? but that's a different issue).

Thoughts 💭?

from aphrodite.

xymostech avatar xymostech commented on August 15, 2024

@kentcdodds I really like that interface. Seems like a great balance between being flexible and still sorta following the "inline-styling way". Some random thoughts:

  • We don't have a good way to distinguish whether something is a CSS property or not. Maybe we could make the key value something like ">descendantClass", where the > indicates a descendant?
  • What would rules for recursion be? I think I would want to limit the depth that you could go to 1 level (so you would never be generating .a .b .c class names).

from aphrodite.

kentcdodds avatar kentcdodds commented on August 15, 2024

Maybe we could make the key value something like ">descendantClass", where the > indicates a descendant

Seems reasonable to me 👍

As for depth, I don't see why aphrodite should concern itself with enforcing a depth ¯_(ツ)_/¯ while I agree developers should probably not go too deep on this, I don't think we'd have to put in extra effort to combat it. It'd turn into a serious code smell before it caused aphrodite trouble I think.

from aphrodite.

RGBboy avatar RGBboy commented on August 15, 2024

If there was an API for defining a ruleset you could easily determine which are properties vs a descendantClass:

const child = StyleSheet.createRuleset({
  color: 'black'
}
const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      child: child
    }
  }
})

const { parent, child } = css(styles)
// parent === 'parent-blahblah'
// child === 'child-xyz'

from aphrodite.

kentcdodds avatar kentcdodds commented on August 15, 2024

Ok, I think that we've got two solid ideas, which should we go with?

Use >decendantClass

const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      '>child': {color: 'black'}
    }
  }
})

const { parent, child } = css(styles.parent)
// parent === 'parent-blahblah'
// child === 'child-xyz'

Have a special API for creating rule sets

const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      child: StyleSheet.createRuleset({color: 'black'}
    }
  }
})

const { parent, child } = css(styles.parent)
// parent === 'parent-blahblah'
// child === 'child-xyz'

I'm not sure which I like better. Would love to get feedback from others.

from aphrodite.

montemishkin avatar montemishkin commented on August 15, 2024

I like the simplicity (for the developer using aphrodite) of >descendentClass. However, I'm wondering if I'm misinterpreting something:

The > symbol is reminiscent of .foo > .bar in true CSS, which indicates a direct descendent, whereas via the examples given above, it seems like you are proposing that this produce css which is selecting any (direct or indirect) descendent.

I think that this would be a misleading choice of api.

Am I misinterpreting?

Also, is the goal to be able to support both direct and arbitrary descendents? It seems like arbitrary descendents should cover all desired use cases since the class names are uniquely generated, whereas direct descendents would not.

from aphrodite.

kentcdodds avatar kentcdodds commented on August 15, 2024

Agreed. That would be misleading... I also think that I prefer the simplicity of the > but not at the expense of it being misleading. Other thoughts of how we could make the API more intuitive and simple?

from aphrodite.

montemishkin avatar montemishkin commented on August 15, 2024

Also, I think I agree with this:

... I think an entirely new method should be used and we shouldn't overload the css method.

cssWithDescendents or css.descendents seems reasonable to me, though I'll have to think on it a bit more.

from aphrodite.

xymostech avatar xymostech commented on August 15, 2024

@montemishkin You are correct, the > symbol would be a bit misleading about what it does if we allowed any descendents. Can anyone think of a better symbol to use there, or another way to indicate children?

from aphrodite.

jlfwong avatar jlfwong commented on August 15, 2024

Would we still want to have this syntax if we supported unsafeRawCss as discussed in #30?

from aphrodite.

kentcdodds avatar kentcdodds commented on August 15, 2024

Is there a reason we couldn't just do:

const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      child: {color: 'black'}
    }
  }
})

const { parent, child } = css.descendents(styles.parent)
// parent === 'parent-blahblah'
// child === 'child-xyz'

I'm pretty sure that we can just say that if the value of a css property is an object, consider it a descendant.

As for the unsafeRawCss, the difference with this feature is that I can still get a className back for child which I can apply to a specific element. The use case with unsafeRawCss that I see is that you can use any kind of selector you want and you don't get a className back for it, so it covers cases where you're not in control of the markup (like with dangerouslySetInnerHTML)

from aphrodite.

xymostech avatar xymostech commented on August 15, 2024

I'm pretty sure that we can just say that if the value of a css property is an object, consider it a descendant.

This wouldn't work, because we sometimes allow real objects as values for css properties, for instance with fontFamily you can use arrays/objects, or with animationName you can provide objects.

from aphrodite.

csilvers avatar csilvers commented on August 15, 2024

Just a drive-by kibbitz, but one possible syntax for "direct or indirect descendant" could be ">>descendentClass".

from aphrodite.

xymostech avatar xymostech commented on August 15, 2024

Okay, I played around with a couple different alternatives, and it looks like we haven't really talked about what gets done with the results of css.descendents. Would it make sense to plug those things into a call to css()? So after something like:

const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      '>>child': {color: 'black'},
    },
  },

  red: {
    color: 'red',
  },
})

const { parent, child } = css.descendents(styles.parent);

Would we be doing className={child + " " + css(styles.red)} or className={css(styles.red, child)}? It sounds like we were talking about the first one, but the second one looks nicer.

I also played around with something that looks like

const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      '>>child': {color: 'black'},
    },
  },

  red: {
    color: 'red',
  },
})

<div className={css(styles.parent)}>
  <div className={css(styles.red, styles.parent.child)}> // <- 

but I don't think that we'll actually be able to generate appropriate class names using this interface (even though it looks even nicer than the css.descendents stuff).

from aphrodite.

zgotsch avatar zgotsch commented on August 15, 2024

Just a drive-by, but I really like the second API, Emily (the one with styles.parent.child). I would also be happy with something like css(styles.red, styles.parent.descendent('child')) or similar.

from aphrodite.

xymostech avatar xymostech commented on August 15, 2024

@zgotsch I like that too. I'm a little scared of a situation like this though:

const styles = StyleSheet.create({
    child: {
        color: 'red',
    },

    base: {
        color: 'black',
        ':hover': {
            '>>child': {
                color: 'blue',
            }
        },
    },

    greenchild: {
        ':hover': {
            '>>child': {
                color: 'green',
            },
        },
    },
});

<div className={css(styles.base, styles.greenchild)}>
    <div className={css(styles.child, styles.base.child)}>

Presumably we would want to generate CSS like...

.child_XXX {
    color: red;
}
.base_XXX-o_O-greenchild_XXX {
    color: black;
}
.base_XXX-o_O-greenchild_XXX:hover .child_XXX {
    color: green;
}

but then you might want

<div className={css(styles.base, styles.greenchild)}>
    <div className={css(styles.child, styles.greenchild.child)}>

to do the same thing? But we can't make styles.base.child and styles.greenchild.child add the same class names, so maybe we have to duplicate the related CSS:

.base_XXX-o_O-greenchild_XXX:hover .child_XXX { // child from base
    color: green;
}
.base_XXX-o_O-greenchild_XXX:hover .child_XXX { // child from greenchild
    color: green;
}

Sorry for brain dumping. I'm gonna play around with this...

from aphrodite.

ranyefet avatar ranyefet commented on August 15, 2024

Is there any update regarding this issue?
I'm using .mySelector:nth-child(3) .childElement which currently I can only set up as a global CSS.

from aphrodite.

jlfwong avatar jlfwong commented on August 15, 2024

@xymostech Continuing discussion about alternatives to the complexity introduced in #61 here.

While the goal of making class names an implementation detail and not controllable by the user, I think the complexity that introduces in #61 is overkill for trying to solve this problem.

Here's my proposed alternative: just use class names. I know this does, to some extent, defeat the niceness of reasoning about whether certain styles/classnames are in use, but I think this use case is sufficiently rare that I think it's okay.

const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      '.someChild': {
        color: 'black',
      },
    },
  },
});

const Component = () => <div className={css(styles.parent)}>
    White background on hover
    <div className='someChild'>
        Black text on hover
    </div>
</div>

Based on the discussion on this issue, this is what people are already doing as a hack, except that if we support this directly then the merging semantics will be correct.

If we so wished, we could generalize this to style arbitrary selectors, and potentially knock out #30 too, something like so:

const styles = StyleSheet.create({
  parent: {
    ':hover': {
      backgroundColor: 'white',
      'selector(.someChild)': {
        color: 'black',
      },
    },
    'selector(a)': {
         // styles for anchors in the element go here.
    }
  },
});

If we went the selector() route, we could preserve correct merging semantics by requiring that the selector specified not contain any spaces or colons. We could include in the docs the reasoning for this syntax existing, with a discouraging note about why you shouldn't use them unless you absolutely have to :)

Thoughts?

from aphrodite.

jlfwong avatar jlfwong commented on August 15, 2024

@ide The syntax I'm proposing would have you do 'selector(a)': { 'selector(img)': { color: linkColor } } for that instead. The generated CSS would do what you want, but having them separated would make the merging semantics more obvious.

from aphrodite.

itajaja avatar itajaja commented on August 15, 2024

@ide I am facing the same problem. I came up with a pretty hacky function to select children:

function children(selector) {
  return `:active, ${selector}`;
}

/// use it like this

StyleSheet.create({
  header: {
    color: 'red',
    [children('li a')]: {
      color: 'blue',
    }
  },
})

Clearly, this works as expected only if the parent component cannot be active, otherwise you'd have to use another selector. I am using it until an official API is designed

from aphrodite.

xymostech avatar xymostech commented on August 15, 2024

@jlfwong :( Oof. I guess that probably happens with my pull request implementation, as well?

from aphrodite.

ide avatar ide commented on August 15, 2024

Is this kind of non-determinism already an issue with multiple styles on the same component? For example:

<div className={css(styles.s1, styles.s2)} />
// or
<div className={classNames(css(styles.s1), css(styles.s2))} />

const styles = StyleSheet.create({
  s1: { color: 'red' },
  s2: { color: 'green' },
});

My understanding is that Aphrodite will emit two class selectors of the same specificity, so the last one emitted wins. I guess technically it's deterministic but hard to reason about since the order of the css() calls matter. But if this is OK, then there could be an argument that it's OK to have similar behavior for the selector(a) syntax too.

from aphrodite.

xymostech avatar xymostech commented on August 15, 2024

@ide That is true, but you should always do css(styles.s1, styles.s2). Actually, I think we have that written up somewhere internal but I don't know if we ever say that in the repo. We might want to put that in the README as a caveat.

The point is, adding two classnames is non-determinism that you can avoid (by combining the calls to css()). The non-determinism that @jlfwong is describing cannot be solved by anything that aphrodite does.

from aphrodite.

xymostech avatar xymostech commented on August 15, 2024

@misterfresh That's fine, but do recognize that it is a hack that's not specifically supported by aphrodite, and might break in future versions.

from aphrodite.

ryan-hamblin avatar ryan-hamblin commented on August 15, 2024

@xymostech if hey way that @misterfresh is handling child elements is a hack, did the main contributors to this project decide on what will be the recommended way to implement styles on nested children in the future? I can use this now as we only have a beta application, but my boss just gave me the nod on using Aphrodite (AWESOME library btw) and I'll need to know the recommended way to handle this in the future. Thanks.

from aphrodite.

sontek avatar sontek commented on August 15, 2024

Yeah, I want to selected all the anchors in a div (need to style component I don't control), whats the status of this? I see its closed but as far as I can tell there was no resolution?

from aphrodite.

jlfwong avatar jlfwong commented on August 15, 2024

@sontek This was closed due to the solution in #95, which is documented here: https://github.com/Khan/aphrodite#advanced-extensions

from aphrodite.

mohsen1 avatar mohsen1 commented on August 15, 2024

This hacky solution might help if you end up in this thread...

.parent {
  --child-background-color: red
}
.parent:hover {
  --child-background-color: blue
}
.child {
  background-color: var(--child-background-color)
}

from aphrodite.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.