Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix z-index stack for .pt-button-group and .pt-control-group elements #592

Merged
merged 14 commits into from
Feb 3, 2017

Conversation

cmslewis
Copy link
Contributor

@cmslewis cmslewis commented Feb 1, 2017

No official issue filed, but originally discovered problems here while working on #249 and #189

Checklist

  • Include tests (it would be awesome to add unit tests to verify the z-index stacking order, but we'd need chai-enzyme to access computed styles for mounted React components - should we add it as a dependency?).
  • Update documentation (@llorca - can you edit examples to your liking?)

Changes proposed in this pull request:

  • Clearly specify the z-index stack of all possible .pt-button-group elements to ensure sharp edges in all cases and states:
  (lowest z-index)

  default buttons
    :disabled, .pt-disabled
    normal
    :focus
    :hover
    :active, .pt-active

  intent buttons
    :disabled, .pt-disabled
    normal
    :focus
    :hover
    :active, .pt-active

  (highest z-index)
  • Clearly specify the z-index stack of all possible .pt-control-group elements to ensure sharp edges in all cases and states:
  (lowest z-index)

  input fields
     :disabled, .pt-disabled
     normal

  regular buttons / pt-select > select
    :disabled, .pt-disabled
    normal
    :focus
    :hover
    :active, .pt-active

  pt-input intent

  intent buttons (selects can't be intented yet, but if they could be, they'd be here too)
    :disabled, .pt-disabled
    normal
    :focus
    :hover
    :active, .pt-active

  input fields (NOT readonly, :disabled, .pt-disabled)
     :focus

  intent input fields (NOT readonly, :disabled, .pt-disabled)
     :focus

  input-group children

  .pt-select::after (i.e. the caret)

  (highest z-index)

Reviewers should focus on:

Definitely look at the diff in "Split" mode, not "Unified" mode.

  • How does the new z-index stack feel?
  • Is it implemented correctly?
  • Are we good with the z-index variable names? Thought about combining them, but the two group types had different enough stacking semantics that it was clearer to spell everything out separately.
  • Are there any weird edge cases with certain combinations of grouped elements?
  • Are there any regressions?

Screenshot

@blueprint-bot
Copy link

De-dup and consolidate CSS rules

Preview: docs | table
Coverage: core | datetime

Copy link
Contributor Author

@cmslewis cmslewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorca Here are some explanatory comments to guide you through the CSS rule-shuffles.

.pt-button,
.pt-input,
.pt-select select {
.pt-select {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved from old line 107.


&:not(.pt-vertical) > * {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this block to the bottom of the file to keep z-index-related stuff in one clump.

margin-right: -$button-border-width;
}
// define disabled styles last so that they override all other
// equally-specific styles.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will delete one of these explanations (it's also explained in the top-level comment above).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@@ -185,11 +340,4 @@ Styleguide components.forms.control-group
border-radius: 0 0 $pt-border-radius $pt-border-radius;
}
}

.pt-dark & {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to line 212 to keep z-index-related rules in one clump.


> :first-child {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to line 289.

.pt-button,
.pt-select {
position: relative;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to the top of the file.

}

&:not([readonly], :disabled, .pt-disabled):focus {
z-index: 6;
border-radius: $pt-border-radius;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to line 197.


// establish new stacking context so focus state covers neighbors
.pt-button:focus,
select:focus {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now handled with higher z-indexes in the same stacking context.

.pt-select::after {
z-index: 7;
z-index: $control-group-z-index-select-caret;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the carets to a z-index all their own that explicitly sits them on top of everything, just to be absolutely safe.

@@ -54,7 +61,7 @@
display: flex;
flex-direction: column;
justify-content: space-between;
height: $pt-grid-size * 13;
height: $pt-grid-size * 50;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels weird that we have to explicitly set a height. Why not just have docs containers expand and contract automatically with the height of the child contents?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they should do that... casual flex box. why do you have to set a height?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already the case. Setting a height here will evenly space out the children so there's spacing in between them. Without this line:
image

So what we can do instead is remove this line and add an additional rule in the same block:

.pt-control-group-example {
  > .pt-control.group:not(:last-child) {
    margin-bottom: $pt-grid-size * 2;
  }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Derp, don't know why I didn't think to try that. Thanks, @llorca.

@adidahiya
Copy link
Contributor

Not opposed to adding chai-enzyme as a devDependency. Sounds like it could be useful.


// have consecutive elements share a border
&:not(.pt-vertical) > * {
margin-right: -$button-border-width;
Copy link
Contributor

@llorca llorca Feb 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually only correct for light theme.

The visuals look different in dark theme, depending on whether you use a button group or single buttons for consecutive buttons (top is bad, bottom is good):
image

So we can keep that line for the light theme, but we need to take care of dark theme. Proposal (to be refactored in the appropriate place):

.pt-dark &:not(.pt-vertical) {
  > * {
    margin-right: 0;
  }

  > .pt-button + .pt-button {
    margin-left: $button-border-width;
  }
}

We also need to handle this situation in the .pt-vertical case.

Copy link
Contributor Author

@cmslewis cmslewis Feb 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed and incorporated as follows:

// have consecutive elements share a border
&:not(.pt-vertical) {
  > * {
    margin-right: -$button-border-width;
  }

  .pt-dark & {
    > * {
      margin-right: 0;
    }

    // conseuctive buttons within a button group look okay out of the box, but
    // we need need to make a small correction for non-grouped buttons
    > .pt-button + .pt-button {
      margin-left: $button-border-width;
    }
  }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! For clarity, maybe also mention that we're basically replicating what's happening in dark button groups?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done 👍

//
// (highest z-index)

$control-group-z-index-input-disabled: 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 Qs about maintainability:

  • if our visual design changes and we ever need to update the stacking of elements in control groups, we can simply modify the values of these variables, right? No need to mess with the selectors and CSS specificities below?

  • these variables wouldn't get exported, and I'm pretty sure they wouldn't be used anywhere else. Maybe we should use a map object instead of single variables for every value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Yep, just change the z-indexes!

  • Moving everything into a list so we can just use the index() helper.

@@ -38,6 +38,13 @@

// Section-Specific Styles

#{section-id("components.button-group")} {
.docs-button-group-example-set .pt-button-group {
margin-top: 20px;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$pt-grid-size * 2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@@ -54,7 +61,7 @@
display: flex;
flex-direction: column;
justify-content: space-between;
height: $pt-grid-size * 13;
height: $pt-grid-size * 50;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already the case. Setting a height here will evenly space out the children so there's spacing in between them. Without this line:
image

So what we can do instead is remove this line and add an additional rule in the same block:

.pt-control-group-example {
  > .pt-control.group:not(:last-child) {
    margin-bottom: $pt-grid-size * 2;
  }
}

"intent-button-default",
"intent-button-focus",
"intent-button-hover",
"intent-button-active";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i see two duplicated lists here. what happened to a single intent-boost variable?

then you go index(list, "disabled") + $intent-boost (pseudocode).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then you could create a mixin that wraps up lines 151-172 and reuse it in the intent block by passing a single fixed "booster" value. @include z-index-state-layers($boost)

Copy link
Contributor Author

@cmslewis cmslewis Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was meant to be just an incremental change. Will play with it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@giladgray chatted and worked through a potential refactor with Antoine. The intent-boost approach works well for button groups, but it's more tricky than helpful in control groups, where differently ordered .pt-input layers get in the way.

One approach we're considering now is to use a single list of named layers that is shared between control groups and button groups, since the relative ordering of elements doesn't actually change between the two contexts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 TLDR: 1 list with everything ($control-group-stack or something), and button groups use a subset of that list via index()

//
// (highest z-index)

$-button-group-z-index-stack:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra - before button. Also, can we just go for $button-group-stack or something simpler? Is z-index really necessary in the name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llorca Fixed. I ended up creating a single $control-group-stack variable, putting it in forms/_common.scss, and importing it into _control-group.scss and _button-group.scss. LMK how that looks.

@blueprint-bot
Copy link

Use a single -group-stack list, pull it out into forms/_common

Preview: docs | landing | table
Coverage: core | datetime

@blueprint-bot
Copy link

Delete unused -state-stack variable

Preview: docs | landing | table
Coverage: core | datetime

margin-right: 0;
}

// conseuctive buttons within a button group look okay out of the box, but
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • consecutive
  • dark theme

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, fixed.

@llorca
Copy link
Contributor

llorca commented Feb 2, 2017

I'm a fan of this approach! Much cleaner, less code, 1 reusable list. Big comment explanation no longer needed, code speaks for itself 💯

<input type="text" class="pt-input" value="from:ggray to:allorca" />
<span class="pt-icon pt-icon-people"></span>
<input type="text" class="pt-input pt-intent-success" placeholder="I am a text input..." style="padding-right:94px" />
<div class="pt-input-action">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.pt-input-action shouldn't be needed when wrapping a .pt-button. InputGroup always uses it because it can't know about the contents but should be safe to remove in these static HTML examples.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. 👍

Copy link
Contributor Author

@cmslewis cmslewis Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just kiddingggg, I need to revert. Forgot that we need these .pt-input-action wrappers in all cases to address issues like #236 and #237. If you delete the .pt-input-action wrapper, you get a weird flicker on focus.

2017-02-02 13 24 49

</div>
<div class="pt-control-group">
<div class="pt-input-group">
<span class="pt-icon pt-icon-people"></span>
<input type="text" class="pt-input" placeholder="Find collaborators..." style="padding-right:94px" />
<input type="text" class="pt-input pt-intent-success" placeholder="I am a text input..." style="padding-right:94px" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the placeholder change? kinda breaks the example metaphor

Copy link
Contributor Author

@cmslewis cmslewis Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the development phase, it was tricky to tell at a glance what was a button and what was an input when either was disabled:

image

Explicit placeholder text helped with that. Totally not wedded to these examples, by the way. They're here just to allow easy testing during the development phase, but I'd prefer CSS unit tests as a longer-term solution.

<button class="pt-button pt-intent-warning">Button</button>
<button class="pt-button pt-intent-success">Button</button>
<button class="pt-button">Button</button>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cmslewis are we planning to ship all these example changes? on one hand, they're useful for testing, but on the other they're somewhat unsightly and are usages that we may not want to promote so visibly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On that note, the 3 examples we currently serve in the docs are more interesting indeed. Once we're confident everything looks good, let's bring back those examples, and maybe we can think of additional 1 or 2 to showcase as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, totally down to revert to our prior examples before merging.

"intent-input-focus",
"input-group-children",

"select-caret";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cmslewis planning to remove this in a future increment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed!

@giladgray
Copy link
Contributor

⭐️ the single stack variable approach works nicely!

@cmslewis
Copy link
Contributor Author

cmslewis commented Feb 2, 2017

@giladgray and @llorca. LMK if this looks good to you now; if so, I'll revert to the previous examples and merge.

@blueprint-bot
Copy link

Address PR comments (comment fixes, delete rule of select caret)

Preview: docs | landing | table
Coverage: core | datetime

// for best visual results, button group and control group elements should be
// stacked in the following order to ensure sharp edges in all cases and states:

$control-group-stack:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it'd be helpful to wrap this in parens, which is the convention for an explicit sass list.

$control-group-stack: (
  ...
);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet. Doing so now.

@cmslewis
Copy link
Contributor Author

cmslewis commented Feb 3, 2017

@giladgray @llorca Oh no! When I reverted the examples to the originals, I found a caret bug on focus:

image

Having position: relative on .pt-select doesn't help; looks like we need that select-caret layer in $control-group-stack after all. Happy to revisit in a future PR though.

@blueprint-bot
Copy link

Bugfix: Reintroduce select-caret z-index to fix focus-state bug

Preview: docs | landing | table
Coverage: core | datetime

@giladgray giladgray added the P0 label Feb 3, 2017
// very particular order for best visual results. in each level of selector
// specificity, we define disabled styles last so that they override all other
// equally-specific styles (e.g. we don't want mouse interactions or focus
// changes to change the z-index of a disabled element).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to use !important for disabled styles like @leebyp is doing in #565?

@giladgray giladgray merged commit a8d6f52 into master Feb 3, 2017
@giladgray giladgray deleted the cl/css-group-z-indexes branch February 3, 2017 22:18
@giladgray giladgray removed the P0 label Feb 3, 2017
@giladgray giladgray mentioned this pull request Feb 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants