Amazon has said the number of demands for user data made by U.S. federal and local law enforcement have increased more during the first half of 2020 than during the same period a year earlier.
The figures show that Amazon received 23% more subpoenas and search warrants, and a 29% increase in court orders compared to the first half of 2019. That includes data collected from its Amazon.com retail storefront, Amazon Echo devices and its Kindle and Fire tablets.
Breaking those figures down, Amazon said it received:
2,416 subpoenas, turning over all or partial user data in 70% of cases.
543 search warrants, turning over all or partial user data in 79% of cases.
146 court orders, turning over all or partial user data in 74% of cases.
The number of requests to the company’s cloud services, Amazon Web Services, also went up compared to a year earlier.
But it’s not clear what caused the rise in U.S. government demands for user data. A spokesperson for Amazon did respond to a request for comment.
The company saw the number of overseas requests drop by about one-third compared to the same period a year earlier. Amazon rejected 92% of the 177 overseas requests it received, turning over partial user data in 10 cases and all requested data in four cases.
Amazon also said it received between 0 and 249 national security requests, flat from previous reports. Justice Department rules on disclosing classified requests only allow companies to respond in numerical ranges.
Amazon was one of the last major tech companies to issue a transparency report, despite mounting pressure from privacy advocates. But its report remains far lighter on details compared to its Silicon Valley rivals.
The following article captures the process of building the Million Developers microsite for Netlify. This project was built by a few folks and we’ve captured some parts of the process of building it here- focusing mainly on the animation aspects, in case any are helpful to others building similar experiences.
The beauty of SVG is you can think of it, and the coordinate system, as a big game of battleship. You’re really thinking in terms of x, y, width, and height.
<div id="app">
<app-login-result-sticky v-if="user.number" />
<app-github-corner />
<app-header />
<!-- this is one big SVG -->
<svg id="timeline" xmlns="http://www.w3.org/2000/svg" :viewBox="timelineAttributes.viewBox">
<!-- this is the desktop path -->
<path
class="cls-1 timeline-path"
transform="translate(16.1 -440.3)"
d="M951.5,7107..."
/>
<!-- this is the path for mobile -->
<app-mobilepath v-if="viewportSize === 'small'" />
<!-- all of the stations, broken down by year -->
<app2016 />
<app2017 />
<app2018 />
<app2019 />
<app2020 />
<!-- the 'you are here' marker, only shown on desktop and if you're logged in -->
<app-youarehere v-if="user.number && viewportSize === 'large'" />
</svg>
</div>
Within the larger app component, we have the large header, but as you can see, the rest is one giant SVG. From there, we broke down the rest of the giant SVG into several components:
Candyland-type paths for both desktop and mobile, shown conditionally by a state in the Vuex store
There are 27 stations, not including their text counterparts, and many decorative components like bushes, trees, and streetlamps, which is a lot to keep track of in one component, so they’re broken down by year
The ‘you are here’ marker, only shown on desktop and if you’re logged in
SVG is wonderfully flexible because not only can we draw absolute and relative shapes and paths within that coordinate system, we can also draw SVGs within SVGs. We just need to defined the x, y, width and height of those SVGs and we can mount them inside the larger SVG, which is exactly what we’re going to do with all these components so that we can adjust their placement whenever needed. The <g> within the components stands for group, you can think of them a little like divs in HTML.
So here’s what this looks like within the year components:
Within these components, you can see a number of patterns:
We have bushes and trees for decoration that we can sprinkle around viax and y values via props
We can have individual station components, which also have two different positioning values, one for large and small devices
We have a text component, which has three available slots, one for the date, and two for two different text lines
We’re also loading in the decorative components synchronously, and loading those heavier SVG stations async
SVG Animation
Header animation for Million Devs
The SVG animation is done with GreenSock (GSAP), with their new ScrollTrigger plugin. I wrote up a guide on how to work with GSAP for their latest 3.0 release earlier this year. If you’re unfamiliar with this library, that might be a good place to start.
Working with the plugin is thankfully straightforward, here is the base of the functionality we’ll need:
First, we’re importing gsap and the package we need, as well as state from the Vuex store. I put the toggleActions and start config settings in the store and passed them into each component because while I was working, I needed to experiment with which point in the UI I wanted to trigger the animations, this kept me from having to configure each component separately.
toggleConfig: play the animation when it passes down the page (another option is to say restart and it will retrigger if you see it again), it pauses when it is out of the viewport (this can slightly help with perf), and that it doesn’t retrigger in reverse when going back up the page.
startConfig is stating that when the center of the element is 90% down from the height of the viewport, to trigger the animation to begin.
These are the settings we decided on for this project, there are many others! You can understand all of the options with this video.
For this particular animation, we needed to treat it a little differently if it was a banner animation which didn’t need to be triggered on scroll or if it was later in the timeline. We passed in a prop and used that to pass in that config depending on the number in props:
Then, for the animation itself, I’m using what’s called a label on the timeline, you can think of it like identifying a point in time on the playhead that you may want to hang animations or functionality off of. We have to make sure we use the number prop for the label too, so we keep the timelines for the header and footer component separated.
There’s a lot going on in the million devs animation so I’ll just isolate one piece of movement to break down: above we have the girls swinging legs. We have both legs swinging separately, both are repeating several times, and that yoyo: true lets GSAP know that I’d like the animation to reverse every other alteration. We’re rotating the legs, but what makes it realistic is the transformOrigin starts at the center top of the leg, so that when it’s rotating, it’s rotating around the knee axis, like knees do :)
Adding an Animation Toggle
We wanted to give users the ability to explore the site without animation, should they have a vestibular disorder, so we created a toggle for the animation play state. The toggle is nothing special- it updates state in the Vuex store through a mutation, as you might expect:
The real updates happen in the topmost App component where we collect all of the animations and triggers, and then adjust them based on the state in the store. We watch the isAnimationDisabled property for changes, and when one occurs, we grab all instances of scrolltrigger animations in the app. We don’t .kill() the animations, which one option, because if we did, we wouldn’t be able to restart them.
Instead, we either set their progress to the final frame if animations are disabled, or if we’re restarting them, we set their progress to 0 so they can restart when they are set to fire on the page. If we had used .restart() here, all of the animations would have played and we wouldn’t see them trigger as we kept going down the page. Best of both worlds!
I am by no means an accessibility expert, so please let me know if I’ve misstepped here- but I did a fair amount of research and testing on this site, and was pretty excited that when I tested on my Macbook via voiceover, the site’s pertinent information was traversable, so I’m sharing what we did to get there.
For the initial SVG that cased everything, we didn’t apply a role so that the screenreader would traverse within it. For the trees and bushes, we applied role="img" so the screenreader would skip it and any of the more detailed stations we applied a unique id and title, which was the first element within the SVG. We also applied role="presentation".
<svg
...
role="presentation"
aria-labelledby="analyticsuklaunch"
>
<title id="analyticsuklaunch">Launch of analytics</title>
The text within the SVG does announce itself as you tab through the page, and the link is found, all of the text is read. This is what that text component looks like, with those slots mentioned above.
The repo is also open source if you want to check out the code or file a PR.
Thanks a million (pun intended) to my coworkers Zach Leatherman and Hugues Tennier who worked on this with me, their input and work was invaluable to the project, it only exists from teamwork to get it over the line! And so much respect to Alejandro Alvarez who did the design, and did a spectacular job. High fives all around. 🙌
Back in May, I learned about Firefox adding masonry to CSS grid. Masonry layouts are something I’ve been wanting to do on my own from scratch for a very long time, but have never known where to start. So, naturally, I checked the demo and then I had a lightbulb moment when I understood how this new proposed CSS feature works.
Support is obviously limited to Firefox for now (and, even there, only behind a flag), but it still offered me enough of a starting point for a JavaScript implementation that would cover browsers that currently lack support.
The way Firefox implements masonry in CSS is by setting either grid-template-rows (as in the example) or grid-template-columns to a value of masonry.
My approach was to use this for supporting browsers (which, again, means just Firefox for now) and create a JavaScript fallback for the rest. Let’s look at how this works using the particular case of an image grid.
First, enable the flag
In order to do this, we go to about:config in Firefox and search for “masonry.” This brings up the layout.css.grid-template-masonry-value.enabled flag, which we enable by double clicking its value from false (the default) to true.
Making sure we can test this feature.
Let’s start with some markup
The HTML structure looks something like this:
<section class="grid--masonry">
<img src="black_cat.jpg" alt="black cat" />
<!-- more such images following -->
</section>
Now, let’s apply some styles
The first thing we do is make the top-level element a CSS grid container. Next, we define a maximum width for our images, let’s say 10em. We also want these images to shrink to whatever space is available for the grid’s content-box if the viewport becomes too narrow to accommodate for a single 10em column grid, so the value we actually set is Min(10em, 100%). Since responsivity is important these days, we don’t bother with a fixed number of columns, but instead auto-fit as many columns of this width as we can:
Note that we’ve used Min() and not min() in order to avoid a Sass conflict.
Well, that’s a grid!
Not a very pretty one though, so let’s force its content to be in the middle horizontally, then add a grid-gap and padding that are both equal to a spacing value ($s). We also set a background to make it easier on the eyes.
$s: .5em;
/* masonry grid styles */
.grid--masonry {
/* same styles as before */
justify-content: center;
grid-gap: $s;
padding: $s
}
/* prettifying styles */
html { background: #555 }
Having prettified the grid a bit, we turn to doing the same for the grid items, which are the images. Let’s apply a filter so they all look a bit more uniform, while giving a little additional flair with slightly rounded corners and a box-shadow.
The only thing we need to do now for browsers that support masonry is to declare it:
.grid--masonry {
/* same styles as before */
grid-template-rows: masonry;
}
While this won’t work in most browsers, it produces the desired result in Firefox with the flag enabled as explained earlier.
grid-template-rows: masonry working in Firefox with the flag enabled (Demo).
But what about the other browsers? That’s where we need a…
JavaScript fallback
In order to be economical with the JavaScript the browser has to run, we first check if there are any .grid--masonry elements on that page and whether the browser has understood and applied the masonry value for grid-template-rows. Note that this is a generic approach that assumes we may have multiple such grids on a page.
let grids = [...document.querySelectorAll('.grid--masonry')];
if(grids.length && getComputedStyle(grids[0]).gridTemplateRows !== 'masonry') {
console.log('boo, masonry not supported ðŸ˜')
}
else console.log('yay, do nothing!')
If the new masonry feature is not supported, we then get the row-gap and the grid items for every masonry grid, then set a number of columns (which is initially 0 for each grid).
Note that we need to make sure the child nodes are element nodes (which means they have a nodeType of 1). Otherwise, we can end up with text nodes consisting of carriage returns in the array of items.
Checking we got the correct number of items and gap (live).
Before proceeding further, we have to ensure the page has loaded and the elements aren’t still moving around. Once we’ve handled that, we take each grid and read its current number of columns. If this is different from the value we already have, then we update the old value and rearrange the grid items.
if(grids.length && getComputedStyle(grids[0]).gridTemplateRows !== 'masonry') {
grids = grids.map(/* same as before */);
function layout() {
grids.forEach(grid => {
/* get the post-resize/ load number of columns */
let ncol = getComputedStyle(grid._el).gridTemplateColumns.split(' ').length;
if(grid.ncol !== ncol) {
grid.ncol = ncol;
console.log('rearrange grid items')
}
});
}
addEventListener('load', e => {
layout(); /* initial load */
addEventListener('resize', layout, false)
}, false);
}
Note that calling the layout() function is something we need to do both on the initial load and on resize.
To rearrange the grid items, the first step is to remove the top margin on all of them (this may have been set to a non-zero value to achieve the masonry effect before the current resize).
If the viewport is narrow enough that we only have one column, we’re done!
Otherwise, we skip the first ncol items and we loop through the rest. For each item considered, we compute the position of the bottom edge of the item above and the current position of its top edge. This allows us to compute how much we need to move it vertically such that its top edge is one grid gap below the bottom edge of the item above.
/* if the number of columns has changed */
if(grid.ncol !== ncol) {
/* update number of columns */
grid.ncol = ncol;
/* revert to initial positioning, no margin */
grid.items.forEach(c => c.style.removeProperty('margin-top'));
/* if we have more than one column */
if(grid.ncol > 1) {
grid.items.slice(ncol).forEach((c, i) => {
let prev_fin = grid.items[i].getBoundingClientRect().bottom /* bottom edge of item above */,
curr_ini = c.getBoundingClientRect().top /* top edge of current item */;
c.style.marginTop = `${prev_fin + grid.gap - curr_ini}px`
})
}
}
We now have a working, cross-browser solution!
A couple of minor improvements
A more realistic structure
In a real world scenario, we’re more likely to have each image wrapped in a link to its full size so that the big image opens in a lightbox (or we navigate to it as a fallback).
<section class='grid--masonry'>
<a href='black_cat_large.jpg'>
<img src='black_cat_small.jpg' alt='black cat'/>
</a>
<!-- and so on, more thumbnails following the first -->
</section>
This means we also need to alter the CSS a bit. While we don’t need to explicitly set a width on the grid items anymore — as they’re now links — we do need to set align-self: start on them because, unlike images, they stretch to cover the entire row height by default, which will throw off our algorithm.
.grid--masonry > * { align-self: start; }
img {
display: block; /* avoid weird extra space at the bottom */
width: 100%;
/* same styles as before */
}
Making the first element stretch across the grid
We can also make the first item stretch horizontally across the entire grid (which means we should probably also limit its height and make sure the image doesn’t overflow or get distorted):
.grid--masonry > :first-child {
grid-column: 1/ -1;
max-height: 29vh;
}
img {
max-height: inherit;
object-fit: cover;
/* same styles as before */
}
We also need to exclude this stretched item by adding another filter criterion when we get the list of grid items:
Let’s say we want to use this solution for something like a blog. We keep the exact same JS and almost the exact same masonry-specific CSS – we only change the maximum width a column may have and drop the max-height restriction for the first item.
As it can be seen from the demo below, our solution also works perfectly in this case where we have a grid of blog posts:
You can also resize the viewport to see how it behaves in this case.
However, if we want the width of the columns to be somewhat flexible, for example, something like this:
The changing width of the grid items combined with the fact that the text content is different for each means that when a certain threshold is crossed, we may get a different number of text lines for a grid item (thus changing the height), but not for the others. And if the number of columns doesn’t change, then the vertical offsets don’t get recomputed and we end up with either overlaps or bigger gaps.
In order to fix this, we need to also recompute the offsets whenever at least one item’s height changes for the current grid. This means we need to also need to test if more than zero items of the current grid have changed their height. And then we need to reset this value at the end of the if block so that we don’t rearrange the items needlessly next time around.
if(grid.ncol !== ncol || grid.mod) {
/* same as before */
grid.mod = 0
}
Alright, but how do we change this grid.mod value? My first idea was to use a ResizeObserver:
if(grids.length && getComputedStyle(grids[0]).gridTemplateRows !== 'masonry') {
let o = new ResizeObserver(entries => {
entries.forEach(entry => {
grids.find(grid => grid._el === entry.target.parentElement).mod = 1
});
});
/* same as before */
addEventListener('load', e => {
/* same as before */
grids.forEach(grid => { grid.items.forEach(c => o.observe(c)) })
}, false)
}
This does the job of rearranging the grid items when necessary even if the number of grid columns doesn’t change. But it also makes even having that if condition pointless!
This is because it changes grid.mod to 1 whenever the heightor the width of at least one item changes. The height of an item changes due to the text reflow, caused by the width changing. But the change in width happens every time we resize the viewport and doesn’t necessarily trigger a change in height.
This is why I eventually decided on storing the previous item heights and checking whether they have changed on resize to determine whether grid.mod remains 0 or not:
function layout() {
grids.forEach(grid => {
grid.items.forEach(c => {
let new_h = c.getBoundingClientRect().height;
if(new_h !== +c.dataset.h) {
c.dataset.h = new_h;
grid.mod++
}
});
/* same as before */
})
}
That’s it! We now have a nice lightweight solution. The minified JavaScript is under 800 bytes, while the strictly masonry-related styles are under 300 bytes.
But, but, but…
What about browser support?
Well, @supports just so happens to have better browser support than any of the newer CSS features used here, so we can put the nice stuff inside it and have a basic, non-masonry grid for non-supporting browsers. This version works all the way back to IE9.
The result in Internet Explorer
It may not look the same, but it looks decent and it’s perfectly functional. Supporting a browser doesn’t mean replicating all the visual candy for it. It means the page works and doesn’t look broken or horrible.
What about the no JavaScript case?
Well, we can apply the fancy styles only if the root element has a js class which we add via JavaScript! Otherwise, we get a basic grid where all the items have the same size.
Welcome back to Tech at Work, where we look at labor, diversity and inclusion. Given the amount of activity in this space, we’re going to ramp this up from bi-weekly to weekly.
This week, we’re looking at the latest action from a group of Amazon warehouse workers in the San Francisco Bay Area, how to avoid Genderify’s massive algorithmic bias fail and the rise of the use of BIPOC, which stands for Black, Indigenous and people of color, and how to properly use the term.
Stay woke
Amazon warehouse workers stage sunrise action
Amazon delivery drivers in the San Francisco Bay Area are kicking off the month by protesting the e-commerce giant’s labor practices related to the COVID-19 pandemic. As part of a caravan, workers plan to head to Amazon’s San Leandro warehouse this morning to pressure the company to shut down the facility for a thorough cleaning.
“They are having COVID cases reported and they’re not being truthful about how many, and they’re not being reported right away,” Amazon worker Adrienne Williams told TechCrunch. “We’re seeing this pattern of Amazon finding out and then not telling people for two weeks so they don’t have to pay anyone.”
Join us as we deliver a petition demanding Amazon shut down DSF4 for deep cleaning in defense of Black and Latinx lives. Systemic racism has led to BIPOC folks being most impacted by COVID. Solidarity with our communities means protecting us from it.https://t.co/Gt7bxTI8l1pic.twitter.com/yPuJP7hAG1
Nothing is more important than health and well-being of our employees, and we are doing everything we can to keep them as safe as possible. We’ve invested over $800 million in the first half of this year implementing 150 significant process changes on COVID-19 safety measures by purchasing items like masks, hand sanitizer, thermal cameras, thermometers, sanitizing wipes, gloves, additional handwashing stations, and adding disinfectant spraying in buildings, procuring COVID testing supplies, and additional janitorial teams.
In addition to shutting down the warehouse for sanitizing, workers are asking for better communication.
“The drivers have no idea if there are ever any cases because we don’t have access to the internal warehouse A to Z communications they have,” Williams, who works at the Richmond warehouse, said. “So we never get the alerts if there are COVID cases. We’re not on that internal communication but we go in those warehouses twice a day to get our shifts and packages.”
Because drivers are generally employed by delivery service partners, Amazon says it does not have direct communication with them. However, Amazon says it immediately notifies the delivery service partner who then communicates with the drivers.
By staging the action so early, the hope is to prevent workers from being able to load delivery vehicles, Williams said.
“If the vans are left in the warehouse, Jeff Bezos takes the financial hit,” she said. “Halting deliveries and keeping them in the warehouse means Amazon gets hit with the bill.”
Lesson for startups: Treat all of your workers with dignity and respect.
from Amazon – TechCrunch https://ift.tt/2XiyiD1
via IFTTT