113 stories
·
1 follower

Making Remote Work: Behind the Scenes at Stack Overflow

1 Share

The logistics of Stack Overflow’s remote-first philosophy

By now you’ve probably heard about Stack Overflow’s commitment to making remote work, well… work. If not, you can read about it here, here, and a whole page of links here. Seriously, we care about it a LOT. At Stack Overflow we want to hire the best person for the job, even if that person is in Salt Lake City, Utah; Berlin, Germany; or São Paulo, Brazil. Promoting remote work promotes diversity, and we are committed to improving both.

However, this isn’t going to be another article about why we believe in remote work. I’m here to tell you about how we pull the whole thing off. I manage workplace operations for the company, and a huge part of that job is making sure that our 85+ remote employees have everything that they need to get their jobs done. So here are my tips for having a successful remote infrastructure:

Have a dedicated remote person (like me!) to be the point person for remote needs.

This is definitely the most important piece of the remote puzzle. You need a person whose stated job is to make sure that remote employees have a specific person for questions, concerns, and problems. When we hire a new remote employee, I tell them that they can come to me with any type of question, and I will try my best to answer it, or at least point them in the right direction.

I get aaaaaall kinds of questions and requests. For instance, I now know how to buy and ship Belgian beer to Poland for an employee that wasn’t able to attend a work event because of a surgery. Also FYI, Uruguay has very strict restrictions on voltage of household items, so getting a crank-lever standing desk to Montevideo was fun (read: not fun).

There also needs to be someone on the executive team (it’s better if there is more than one someone) consistently asking “What about the remotes?” When you have a new “x,” the execs should be thinking about how that impacts all employees, including remotes.

Have your People and Finance Teams homed in on remote needs

Companies will encounter a wide range of odd legalities in dealing with remote employees, especially with international folks. Did you know that in Romania if someone is going to have a home office, they have to have it inspected by a fire marshal? And in the U.S. we have a rule called “prevailing wage determination” that requires some remote employees to post a paper notice in their home for at least 10 days. (I like to think they put it in a laundry room or over the toilet.)

It definitely helps to have an HR department that is certified in immigration and visa issues. At Stack Overflow we are lucky enough to have a lawyer on our People Team along with other trained HR counsels for employees to go to when these issues arise. The same goes for finance departments. The taxation alone is a handful with multiple states/countries. You need an entity for each state, which requires considerable forethought. You also have to file payrolls properly and that varies depending on location. No one ever accused the IRS of being simple!

Remote first

We’ve already touched on this in the other blog posts, but it bears repeating: If one person in the meeting is remote, we’re all remote. If there are 5 people in a conference room and 2 people dialing in remotely, it is very easy for the folks in the meeting room to forget about the people dialed in. You can eliminate that problem by having everyone call in, if possible. After a while it is second nature to jump on a call instead of finding a room. Currently in the Stack Overflow NYC office it is really common to walk around and see two people on a hangout even though their offices are next to each other! (It is also possible that these people are just lazy…)

Try to recreate office fun stuff in a remote capacity

We have regular “Bev Bashes” and holiday parties in the offices, but what about the remotes? How do you give them fun stuff even though they are scattered all over the world?

For starters, we have “Remote Bev Bashes” over Zoom every Friday. The “Bev Baron” for that week (nominated by the last Bev Baron) schedules a Zoom call and sets a theme. Employees grab a beverage of their choice and just chat. You can stay for just a few minutes to say hi, or you can be a part of the entire thing.

Additionally, each office has a pretty fancy Holiday party in December, so we of course invite any remotes that are near the offices. If they are too far away to attend, we give a $200 stipend so that they can have their own festivities, whether it’s a really nice night out with their partners or just a big dinner for family. We don’t care as long as the employee does something yummy with it.

Don’t forget: If you make swag (t-shirts, hats, new socks etc) make sure that you send them to the remotes too. It may seem like a lot of work (believe me, with 87 remotes, it is a lot of shipping), but it is definitely worth it!

Bring them all together

Once a year, we bring all of our remote employees together for a week for some much needed face-time. It’s a different city each time, and there are usually 3 days of Executive Keynotes, Tiny Talks, and many many games of Werewolf. We talk about the state of the company and new projects and teams, but we vow not to make any huge decisions or business altering plans. The focus is on camaraderie, not profits. We look forward to it all year, and it’s definitely a blast. No pressure on the person that has to plan it, right? Read: That’s also me.

We also have smaller mini-meetups for teams to come together when needed, but those are coordinated by the teams themselves and centered around one of our three offices.

Make their home workspaces as awesome as the offices

When a new remote employee is hired, I contact them for their furniture choices. They get their choice of the exact same desks and chairs that we have in the offices, free of charge. We want them to be as comfortable as possible, and that means if they want to stand instead of sit, no problem. Our IT dept sets them up with all of their tech, and I set them up with everything else.

Need a filing cabinet? Cool. You want a different type of desk that is bright pink and sings? Hey, it’s your house. We re-create the in-office set-ups as much as possible so that our remote employees never feel less valued than everyone else.

Want to go remote yourself?

Love remote work or the idea of it? Check out our Live + Work Anywhere job listings on Stack Overflow Jobs.

The post Making Remote Work: Behind the Scenes at Stack Overflow appeared first on Stack Overflow Blog.

Read the whole story
grudes
8 days ago
reply
Share this story
Delete

An event for CSS position:sticky

1 Share

An event for CSS position:sticky

TL;DR

Here's a secret: You may not need scroll events in your next app. Using an IntersectionObserver, I show how you can fire a custom event when position:sticky elements become fixed or when they stop sticking. All without the use of scroll listeners. There's even an awesome demo to prove it:

View demo | Source

Introducing the sticky-change event

An event is the the missing feature of CSS position:sticky.

One of the practical limitations of using CSS sticky position is that it doesn't provide a platform signal to know when the property is active. In other words, there's no event to know when an element becomes sticky or when it stops being sticky.

Take the following example, which fixes a <div class="sticky"> 10px from the top of its parent container:

.sticky {
  position: sticky;
  top: 10px;
}

Wouldn't it be nice if the browser told when the elements hits that mark? Apparently I'm not the only one that thinks so. A signal for position:sticky could unlock a number of use cases:

  1. Apply a drop shadow to a banner as it sticks.
  2. As a user reads through your content, record analytics hits to know their progress.
  3. As a user scrolls the page, update a floating TOC widget to the current section.

With these use cases in mind, we've crafted an end goal: create an event that fires when a position:sticky element becomes fixed. Let's call it the sticky-change event:

document.addEventListener('sticky-change', e => {
  const header = e.detail.target;  // header became sticky or stopped sticking.
  const sticking = e.detail.stuck; // true when header is sticky.
  header.classList.toggle('shadow', sticking); // add drop shadow when sticking.

  document.querySelector('.who-is-sticking').textContent = header.textContent;
});

The demo uses this event to headers a drop shadow when they become fixed. It also updates the new title at the top of the page.

In the demo, effects are applied without scrollevents.

Scroll effects without scroll events?

Terminology
Structure of the page.

Let's get some terminology out of the way so I can refer to these names throughout the rest of the post:

  1. Scrolling container - the content area (visible viewport) containing the list of "blog posts".
  2. Headers - blue title in each section that have position:sticky.
  3. Sticky sections - each content section. The text that scrolls under the sticky headers.
  4. "Sticky mode" - when position:sticky is applying to the element.

To know which header enters "sticky mode", we need some way of determining the scroll offset of the scrolling container. That would give us a way to calculate the header that's currently showing. However, that gets pretty tricky to do without scroll events :) The other problem is that position:sticky removes the element from layout when it becomes fixed.

So without scroll events, we've lost the ability to perform layout-related calculations on the headers.

Adding dumby DOM to determine scroll position

Instead of scroll events, we're going to use an IntersectionObserver to determine when headers enter and exit sticky mode. Adding two nodes (aka sentinels) in each sticky section, one at the top and one at the bottom, will act as waypoints for figuring out scroll position. As these markers enter and leave the container, their visiblitiy changes and Intersection Observer fires a callback.

Without sentinel elements showing
The hidden sentinel elements.

We need two sentinels to cover four cases of scrolling up and down:

  1. Scrolling down - header becomes sticky when its top sentinel crosses the top of the container.
  2. Scrolling down - header leaves sticky mode as it reaches the bottom of the section and its bottom sentinel crosses the top of the container.
  3. Scrolling up - header leaves sticky mode when its top sentinel scrolls back into view from the top.
  4. Scrolling up - header becomes sticky as its bottom sentinel crosses back into view from the top.

It's helpful to see a screencast of 1-4 in the order they happen:

Intersection Observers fire callbacks when the sentinels enter/leave the scroll container.

The CSS

The sentinels are positioned at the top and bottom of each section. .sticky_sentinel--top sits on the top of the header while .sticky_sentinel--bottom rests at the bottom of the section:

Bottom sentinel reaching its threshold
Position of the top and bottom sentinel elements.
:root {
  --default-padding: 16px;
  --header-height: 80px;
}
.sticky {
  position: sticky;
  top: 10px; /* adjust sentinel height/positioning based on this position. */
  height: var(--header-height);
  padding: 0 var(--default-padding);
}
.sticky_sentinel {
  position: absolute;
  left: 0;
  right: 0; /* needs dimensions */
  visibility: hidden;
}
.sticky_sentinel--top {
  /* Adjust the height and top values based on your on your sticky top position.
  e.g. make the height bigger and adjust the top so observeHeaders()'s
  IntersectionObserver fires as soon as the bottom of the sentinel crosses the
  top of the intersection container. */
  height: 40px;
  top: -24px;
}
.sticky_sentinel--bottom {
  /* Height should match the top of the header when it's at the bottom of the
  intersection container. */
  height: calc(var(--header-height) + var(--default-padding));
  bottom: 0;
}

Setting up the Intersection Observers

Intersection Observers asynchronously observe changes in the intersection of a target element and the document viewport or a parent container. In our case, we're observe intersections with a parent container.

The magic sauce is IntersectionObserver. Each sentinel gets an IntersectionObserver to observer its intersection visibility within the scroll container. When a sentinel scrolls into the visible viewport, we know a header become fixed or stopped being sticky. Likewise, when a sentinel exits the viewport.

First, I set up observers for the header and footer sentinels:

/**
 * Notifies when elements w/ the `sticky` class begin to stick or stop sticking.
 * Note: the elements should be children of `container`.
 * @param {!Element} container
 */
function observeStickyHeaderChanges(container) {
  observeHeaders(container);
  observeFooters(container);
}

observeStickyHeaderChanges(document.querySelector('#scroll-container'));

Then, I added an observer to fire when .sticky_sentinel--top elements pass through the top of the scrolling container (in either direction). The observeHeaders function creates the top sentinels and adds them to each section. The observer calculates the intersection of the sentinel with top of the container and decides if it's entering or leaving the viewport. That information determines if the section header is sticking or not.

/**
 * Sets up an intersection observer to notify when elements with the class
 * `.sticky_sentinel--top` become visible/invisible at the top of the container.
 * @param {!Element} container
 */
function observeHeaders(container) {
  const observer = new IntersectionObserver((records, observer) => {
    for (const record of records) {
      const targetInfo = record.boundingClientRect;
      const stickyTarget = record.target.parentElement.querySelector('.sticky');
      const rootBoundsInfo = record.rootBounds;

      // Started sticking.
      if (targetInfo.bottom < rootBoundsInfo.top) {
        fireEvent(true, stickyTarget);
      }

      // Stopped sticking.
      if (targetInfo.bottom >= rootBoundsInfo.top &&
          targetInfo.bottom < rootBoundsInfo.bottom) {
       fireEvent(false, stickyTarget);
      }
    }
  }, {threshold: [0], root: container});

  // Add the top sentinels to each section and attach an observer.
  const sentinels = addSentinels(container, 'sticky_sentinel--top');
  sentinels.forEach(el => observer.observe(el));
}

The observer is configured with threshold: [0] so its callback fires as soon as the sentinel becomes visible.

The process is similar for the bottom sentinel (.sticky_sentinel--bottom). A second observer is created to fire when the footers pass through the bottom of the scrolling container. The observeFooters function creates the sentinel nodes and attaches them to each section. The observer calculates the intersection of the sentinel with bottom of the container and decides if it's entering or leaving. That information determines if the section header is sticking or not.

/**
 * Sets up an intersection observer to notify when elements with the class
 * `.sticky_sentinel--bottom` become visible/invisible at the botton of the
 * container.
 * @param {!Element} container
 */
function observeFooters(container) {
  const observer = new IntersectionObserver((records, observer) => {
    for (const record of records) {
      const targetInfo = record.boundingClientRect;
      const stickyTarget = record.target.parentElement.querySelector('.sticky');
      const rootBoundsInfo = record.rootBounds;
      const ratio = record.intersectionRatio;

      // Started sticking.
      if (targetInfo.bottom > rootBoundsInfo.top && ratio === 1) {
        fireEvent(true, stickyTarget);
      }

      // Stopped sticking.
      if (targetInfo.top < rootBoundsInfo.top &&
          targetInfo.bottom < rootBoundsInfo.bottom) {
        fireEvent(false, stickyTarget);
      }
    }
  }, {threshold: [1], root: container});

  // Add the bottom sentinels to each section and attach an observer.
  const sentinels = addSentinels(container, 'sticky_sentinel--bottom');
  sentinels.forEach(el => observer.observe(el));
}

The observer is configured with threshold: [1] so its callback fires when the entire node is within view.

Lastly, there's my two utilities for firing the sticky-change custom event and generating the sentinels:

/**
 * @param {!Element} container
 * @param {string} className
 */
function addSentinels(container, className) {
  return Array.from(container.querySelectorAll('.sticky')).map(el => {
    const sentinel = document.createElement('div');
    sentinel.classList.add('sticky_sentinel', className);
    return el.parentElement.appendChild(sentinel);
  });
}

/**
 * Dispatches the `sticky-event` custom event on the target element.
 * @param {boolean} stuck True if `target` is sticky.
 * @param {!Element} target Element to fire the event on.
 */
function fireEvent(stuck, target) {
  const e = new CustomEvent('sticky-change', {detail: {stuck, target}});
  document.dispatchEvent(e);
}

That's it!

Final demo

We created a custom event when elements with position:sticky become fixed and added scroll effects without the use of scroll events.

View demo | Source

Conclusion

I've often wondered if IntersectionObserver would be a helpful tool to replace some of the scroll event-based UI patterns that have developed over the years. Turns out the answer is yes and no. The semantics of the IntersectionObserver API make it hard to use for everything. But as I've shown here, you can use it for some interesting techniques.

Another way to detect style changes?

Not really. What we needed was a way to observe style changes on a DOM element. Unfortunately, there's nothing in the web platform APIs that allow you to watch style changes.

A MutationObserver would be a logical first choice but that doesn't work for most cases. For example, in the demo, we'd receive a callback when the sticky class is added to an element, but not when the element's computed style changes. Recall that the sticky class was already declared on page load.

In the future, a "Style Mutation Observer" extension to Mutation Observers might be useful to observe changes to an element's computed styles. position: sticky.

Read the whole story
grudes
23 days ago
reply
Share this story
Delete

Viewing Software Engineers Through The Lens Of The Milgram Experiment On Obedience To Authority Figures

1 Share
Ben Nadel considers the software engineer through the lens of the Milgram Experiment on Obedience to Authority Figures. And, attempts to build better empathy and understanding through the constraints of culture as opposed to the intent of the individual....
Read the whole story
grudes
29 days ago
reply
Share this story
Delete

Dwelling On The Past: The Importance Of Project Retrospectives (Part 1)

1 Share

   

We should always look for opportunities to grow and improve. Retrospectives and reflections allow you to codify what you’ve learned from experience, to document mistakes and avoid future ones, and to increase your potential to grow in the future.

Dwelling On The Past: The Importance Of Project Retrospectives (Part 1)

Agile methodologies typically include time for retrospectives throughout a project. Regardless of your methodology, all teams would benefit from having a retrospective at the conclusion of a project.

The post Dwelling On The Past: The Importance Of Project Retrospectives (Part 1) appeared first on Smashing Magazine.

Read the whole story
grudes
43 days ago
reply
Share this story
Delete

Knock out a quick win

1 Share

The smallest action as a leader can have the biggest impact.

“I had no idea it mattered so much.”

A CEO said this to me about a year ago. I’d run into him at a conference. As we sat down at lunch together, he shared something that had happened to him recently…

A few months prior, he had asked his team a question through Know Your Company (they’re a happy customer!). The question was:

“Would you like a new office chair?”

The CEO initially thought the question was a little silly, to be frank. Did office chairs really matter? He doubted anything meaningful would come of the question, but he decided to ask it anyway.

Turns out, every single person in the office (they’re about a 14-person company) responded with, “Yes, I’d like a new office chair.”

Not only that, but many of them wrote lengthy, in-depth responses about how unhappy their chairs were making them — how it hurt their lower backs, how it kept them from concentrating and focusing on their work.

“I was shocked,” the CEO told me. “Something I thought was so small, was actually pretty big.”

So he decided to do something about it. The following day, the CEO asked everyone to pick out their own office chairs via Amazon or another site online. The chairs got shipped to the office the next week. Everyone spent a few hours all together during one afternoon, assembling their new office chair, laughing and joking with one another.

To the CEO’s surprise, it became a bonding event. He described:

“That single moment alone — getting people new offices chairs — boosted morale in the company more than anything else I’ve tried. The energy of the office has completely shifted since then.”

He continued:

“I’ve spent tens of thousands of dollars on training programs and all sorts of employee engagement initiatives… and office chairs was the thing that did it?!”

The CEO couldn’t help but laugh. He never expected that acting on something so small would make such a big difference.

But it did. And it makes sense.

Taking action on something small is the single most effective way to increase morale in your company. When you do something that an employee suggests, you’re literally sending the message: “I want things to be as YOU would like them to be.” That’s powerful. Actions truly speak louder than words in this case.

It may sound obvious, but we often forget this as leaders: People share feedback because they want some form of action taken. No one is saying they’d like a new office chair just for the sake of saying it — they’d like the issue addressed somehow. Doing something (even if it is just getting new office chairs) reinforces that you’re listening as a leader, and encourages folks to speak up and be honest with you in the future.

Consider it a “quick win.” No matter how small, it makes a real difference.

Is there something small that was requested by an employee, that you haven’t gotten around to yet? Knock out the quick win.

Is there a decision that you’ve been sitting on, because you didn’t think it was that important? Knock out the quick win.

Employees value responsiveness. They’ll feel encouraged that their words led to action. That momentum will have a positive effect on morale.

Even if it’s office chairs, it’s a quick win. Knock it out.

I wrote this piece as the latest chapter in our Knowledge Center. Each week, we release a new chapter on how to create an open, honest company culture. To get each chapter sent straight to your inbox, sign up below…

P.S.: Please feel free to share + give this piece 👏 so others can find it too. Thanks 😊 (And you can always say hi at @cjlew23.)


Knock out a quick win was originally published in Signal v. Noise on Medium, where people are continuing the conversation by highlighting and responding to this story.

Read the whole story
grudes
44 days ago
reply
Share this story
Delete

Separate Form Submit Buttons That Go To Different URLs

2 Shares

This came up the other day. I forget where, but I jotted it down in my little notepad for blog post ideas. I wrote it down because what I was overhearing was way over-complicating things.

Say you have a form like this:

<form action="/submit">
  
  <!-- inputs and stuff -->

  <input type="submit" value="Submit">

</form>

When you submit that form, it's going to go to the URL `/submit`. Say you need another submit button that submits to a different URL. It doesn't matter why. There is always a reason for things. The web is a big place and all that.

I web searched around for other ways people have tried handling this.

One way was to give up on submitting to different URL's, but give each submit button a shared name but different value, then check for that value when processing in order to do different things.

<input type="submit" name="action" value="Value-1">
<input type="submit" name="action" value="Value-2">

You could read that value during your processing and redirect if you desired. But that's a workaround for the stated problem.

Another way was to use JavaScript to change the action of the <form> when the button was clicked. There are any number of ways to do that, but it boils down to:

<form name="form">

  <!-- inputs and stuff -->

  <input type="submit" onclick="javascript: form.action='/submit';">
  <input type="submit" onclick="javascript: form.action='/submit-2';"> 

</form>

Which of course relies on JavaScript to work, which isn't a huge deal, but isn't quite as progressive enhancement friendly as it could be.

The correct answer (if I may be so bold) is that HTML has this covered for you already. Perhaps it's not as well-known as it should be. Hence the blog post here, I suppose.

It's all about the formaction attribute, which you can put directly on submit buttons, which overrides the action on the form itself.

<form action="/submit">
  
  <input type="submit" value="Submit">
  
  <input type="submit" value="Go Elsewhere" formaction="/elsewhere">
  
</form>

That's all. Carry on.


Separate Form Submit Buttons That Go To Different URLs is a post from CSS-Tricks

Read the whole story
grudes
50 days ago
reply
Share this story
Delete
Next Page of Stories