Преглед на файлове

Uppy 2.0.0 release post (#3100)

* WIP

* Write section about TS type changes

* Fix typos

* Ignore some eslint code block warnings

* update authors info

* add margin for PostAuthor

* edit post, add draft cover

* Add section about S3 pre-sign batches

* Give attribution to martin-brennan

* Add links

* Add missing link

* Tiny fixes

* Apply suggestions from code review

* Update 2021-XX-XX.md

* Update image and add table of contents

* Improve sentences, add highlights since 1.0

* Update hightlights since 1.0

* Change file name, update title, fix description and image on /blog

* Update hightlights since 1.0

* Different hr md syntax, just to unbreak syntax highlighting in my vscode

It started treating the remainder of the post as yaml

* Add support notice for 1.0

* Iteration

* Disable disqus comments

* Better post date appearance + better "Continue reading" link

* Add .PostAuthors Wrapper

* Tweak .PostAuthor

* Underline author name on hover

* Nicer Author(s) section

* Not that pale date

* iterate on the 2.0 blog post more

* Remove duplicate sentence

* add cut: <!--more-->

* Fix image placement

* Remove merge conflict

* Remove `lerna` mention as notable upgrade

Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>

* Update website/src/_posts/2021-08-30.31.md

Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>

* Premature save

Work in progress!

* A round of polish

* Update website/src/_posts/2021-08-30.31.md

Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>

* Update website/src/_posts/2021-08-30.31.md

Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>

Co-authored-by: Artur Paikin <artur@arturpaikin.com>
Co-authored-by: Kevin van Zonneveld <kevin@vanzonneveld.net>
Co-authored-by: Alexander Zaytsev <nqst@users.noreply.github.com>
Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
Co-authored-by: AJvanLoon <ajvanloon@gmail.com>
Merlijn Vos преди 3 години
родител
ревизия
6931ccd5e3

+ 23 - 1
website/src/_data/authors.yml

@@ -2,7 +2,7 @@ arturi:
   email: 'artur@transloadit.com'
   name: 'Artur Paikin'
   id: 'artur'
-  tagline: 'Frontend developer and interface designer'
+  tagline: 'Developer'
 ife:
   email: 'ifedapo@transloadit.com'
   name: 'Ifedapo Olarewaju'
@@ -38,3 +38,25 @@ hedgerh:
   name: 'Harry Hedger'
   id: 'harry'
   tagline: 'Frontend developer'
+aduh95:
+  email: 'antonie@transloadit.com'
+  name: 'Antoine du Hamel'
+  id: 'aduh95'
+  tagline: 'Developer'
+murderlon:
+  email: 'merlijn@transloadit.com'
+  name: 'Merlijn Vos'
+  id: 'merlijn'
+  tagline: 'Developer'
+mifi:
+  email: 'mikael@transloadit.com'
+  name: 'Mikael Finstad'
+  id: 'mikael'
+  tagline: 'Developer'
+andrew:
+  email: 'andrew@transloadit.com'
+  name: 'Andrew Kachnic'
+  id: 'andrew'
+  tagline: 'Developer'
+
+

+ 246 - 0
website/src/_posts/2021-08-30.31.md

@@ -0,0 +1,246 @@
+---
+title: "Uppy 2.0.0: a pup no more"
+date: 2021-08-30
+author: 
+  - aduh95
+  - murderlon
+  - arturi
+  - renee
+  - mifi
+  - andrew
+image: https://uppy.io/images/blog/2.0/uppy-2-0-cover.jpg
+published: true
+---
+
+Today, our tails are positively wagging with excitement about the release of Uppy 2.0! This latest version is on average 25% smaller and up to a thousand times faster, thanks to dropping support for IE11 and a lot of refactoring work. We’ve upgraded many dependencies, most notably Preact 8 to Preact X, greatly improved TypeScript support and screen reader accessibility, paid technical debt, and added support for multiple messages in the Informer plugin.
+
+Chow down on all the juicy bits and pieces inside!
+
+![Uppy 2.0 cover banner](/images/blog/2.0/uppy-2-0-cover.jpg)
+
+<!--more-->
+
+Uppy is a sleek, modular JavaScript file uploader that integrates seamlessly with any application. It is made for developers who want to provide their users with the ability to see image previews, edit metadata, pick large files directly from Dropbox, restore selected files when a tab was accidentally closed, or crop an image in-browser before sending.
+
+* * *
+
+Ever since the first introduction of Uppy [five years ago](/blog/2016/07/uppy-begins/) (or 36 in [dog-years](https://www.akc.org/expert-advice/health/how-to-calculate-dog-years-to-human-years/)), we’ve always referred to our project as ‘the next open source file uploader for web browsers’. The release of [Uppy 1.0](/blog/2019/04/1.0/) a little over two years ago, however, soon led to a steep increase in adoption. Uppy now boasts over [24,000 stargazers on GitHub](https://github.com/transloadit/uppy/stargazers), making it the undisputed top dog in the world of file uploaders 🌍
+
+With that in mind, we felt it was high time to give Uppy some more much-needed trimming. We want to take this opportunity to break with the past, to make the project leaner – and to pave the way for an even brighter future for Uppy!
+
+![Uppy 2.0 UI with files](/images/blog/2.0/uppy-ui-with-files.jpg)
+
+## Table of Contents
+
+- [Highlights since 1.0](#Highlights-since-1-0)
+- [Smaller bundles](#Smaller-bundles)
+- [Faster](#Faster)
+- [Preact X and upgraded dependencies](#Preact-X-and-upgraded-dependencies)
+- [`Plugin` is replaced with `BasePlugin` and `UIPlugin`](#Plugin-is-replaced-with-BasePlugin-and-UIPlugin)
+- [Strict TypeScript types by default](#Strict-TypeScript-types-by-default)
+- [Batch pre-signing URL's for AWS S3 Multipart](#Batch-pre-signing-URL’s-for-AWS-S3-Multipart)
+- [And more](#And-more)
+- [What future remains for 1.0?](#What-future-remains-for-1-0?)
+- [That's it](#That’s-it)
+
+## Highlights since 1.0
+
+A lot of things have happened since we released Uppy 1.0 in April of 2019. In case you have missed some of our 1.x releases, here is a small overview. We have added:
+
+- Official integrations for [Vue](/blog/2020/11/1.23/) ([3](/blog/2021/04/1.27/)), [Angular](/blog/2021/07/1.30/), and [Svelte](/blog/2020/12/1.24/)
+- [Hooks](http://localhost:4000/blog/2020/12/1.24/) for the React integration
+- Support for picking files from [Box](/blog/2020/11/1.23/), [Unsplash](/blog/2020/10/1.22/), [Facebook](/blog/2020/04/1.10/), and [OneDrive](/blog/2020/04/1.10/) 
+- [“Ghost” files](/blog/2021/05/1.29/), as part of a revamped Golden Retriever plugin, which makes recovering lost files even more intuitive
+- Support for 25 more languages (bringing it to a total of [37](/docs/locales/#List-of-locale-packs))
+- [Dark mode](/blog/2020/04/1.13/) 
+- an [Image Editor](/blog/2020/07/1.18-image-editor/)
+- And four new core team members: [@ajkachnic](https://github.com/ajkachnic), [@mifi](https://github.com/mifi), [@aduh95](https://github.com/aduh95), and [@murderlon](https://github.com/murderlon)
+
+## Smaller bundles
+
+With 2.0, following in the footsteps of Microsoft, we are dropping support for IE11. As a result, we are able to remove all built-in polyfills, and the new bundle size is **25% smaller**! If you want your app to still support older browsers (such as IE11), you may need to add the following polyfills to your bundle:
+
+- [abortcontroller-polyfill](https://github.com/mo/abortcontroller-polyfill)
+- [core-js](https://github.com/zloirock/core-js)
+- [md-gum-polyfill](https://github.com/mozdevs/mediaDevices-getUserMedia-polyfill)
+- [resize-observer-polyfill](https://github.com/que-etc/resize-observer-polyfill)
+- [whatwg-fetch](https://github.com/github/fetch)
+
+If you're using a bundler, you need import these before Uppy:
+
+```js
+import 'core-js'
+import 'whatwg-fetch'
+import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'
+// Order matters here: AbortController needs fetch, which needs Promise (provided by core-js).
+
+import 'md-gum-polyfill'
+import ResizeObserver from 'resize-observer-polyfill'
+
+window.ResizeObserver ??= ResizeObserver
+
+export { default } from '@uppy/core'
+export * from '@uppy/core'
+```
+
+If you're using Uppy from a CDN, we now provide two bundles: one for up-to-date browsers that do not include polyfills and use modern syntax, and one for legacy browsers. When migrating, be mindful about the types of browsers you want to support:
+
+```html
+<!-- Modern browsers (recommended) -->
+<script src="https://releases.transloadit.com/uppy/v2.0.1/uppy.min.js"></script>
+
+<!-- Legacy browsers (IE11+) -->
+<script nomodule src="https://releases.transloadit.com/uppy/v2.0.1/uppy.legacy.min.js"></script>
+<script type="module">import "https://releases.transloadit.com/uppy/v2.0.1/uppy.min.js";</script>
+```
+
+Please note that while you may be able to get 2.0 to work in IE11 this way, we do not officially support it anymore.
+
+## Faster
+
+Uppy now loads faster thanks to the decreased bundle size. With Uppy 1.0, adding many files (hundreds or even thousands) used to take dozens of seconds. Uppy 2.0 does the same thing in mere milliseconds! So, at least for this specific use case, we feel comfortable in claiming that you may see your loading times go **up to a thousand times faster**.
+
+This was made possible by avoiding having to re-render all the file components whenever something changes, using `memoize` and `virtual-list` (allowing us to only render what is actually visible on screen). In addition, multiple files are now added to state in one go via `uppy.addFiles(Array)` as opposed to before, when `uppy.addFile(File)` + `uppy.setState` were being called in a loop.
+
+Before optimizations:
+
+![](/images/blog/2.0/uppy-performance-before.gif)
+
+And after:
+
+![](/images/blog/2.0/uppy-performance-after.gif)
+
+## Preact X and upgraded dependencies
+
+We’ve upgraded almost all of Uppy’s dependencies. This includes the migration to Preact X. All plugins that depend on Preact have been upgraded from `8.2.9` to the latest version `10.5.13`. If you’d like your custom plugin to continue working with Uppy 2.0, it also needs to be using latest version of Preact.
+
+Other notable upgrades include browserify to `v10`, typescript to `v4.3`, autoprefixer to `v10`, and lerna to `v4`.
+
+## `Plugin` is replaced with `BasePlugin` and `UIPlugin`
+
+[`@uppy/core`][core] provided a `Plugin` class for creating plugins. This was used for any official plugin, but also for users who want to create their own custom plugin. However, `Plugin` always came bundled with Preact, even if the plugin itself didn't add any UI elements.
+
+As of Uppy 2.0.0, `Plugin` has been replaced with `BasePlugin` and `UIPlugin`. `BasePlugin` is the minimum you need to create a plugin and `UIPlugin` adds Preact for rendering user interfaces.
+
+Interested in creating your own plugin? Check out the [“writing plugins”](/docs/writing-plugins) guide.
+
+## Strict TypeScript types by default
+
+Uppy used to have loose types by default and strict types as an opt-in. The default export was a function that returned the `Uppy` class, and the types came bundled with the default export (`Uppy.SomeType`).
+
+```ts
+import Uppy from '@uppy/core'
+import Tus from '@uppy/tus'
+
+const uppy = Uppy<Uppy.StrictTypes>()
+
+uppy.use(Tus, {
+  invalidOption: null, // this will make the compilation fail!
+})
+```
+
+Uppy is now strictly typed by default and loose types have been removed. The default export is the `Uppy` class and not a function. This means you need to call `Uppy` with the `new` keyword when initializing it.
+
+```ts
+// ...
+
+const uppy = new Uppy()
+
+uppy.use(Tus, {
+  invalidOption: null, // this will make the compilation fail!
+})
+```
+
+Uppy types are now individual exports and should be imported separately.
+
+<!-- eslint-disable @typescript-eslint/no-unused-vars -->
+```ts
+import type { PluginOptions, UIPlugin, PluginTarget } from '@uppy/core'
+```
+
+### Event types
+
+[`@uppy/core`][core] provides an [`.on`](/docs/uppy/#uppy-on-39-event-39-action) method to listen to [events](/docs/uppy/#Events). The types for these events were loose and allowed for invalid events to be passed, such as `uppy.on('upload-errrOOOoooOOOOOrrrr')`.
+
+Events have received a big update thanks to [@Hawxy](https://github.com/Hawxy), making them more strict and accurate.
+
+A breaking change was required to make this happen:
+
+<!-- eslint-disable @typescript-eslint/no-unused-vars -->
+```ts
+// Before:
+
+type Meta = { myCustomMetadata: string }
+
+// Invalid event
+uppy.on<Meta>('upload-errrOOOoooOOOOOrrrr', () => {
+  // ...
+})
+
+// After:
+
+// Normal event signature
+uppy.on('complete', (result) => {
+  const successResults = result.successful
+})
+
+// Custom signature
+type Meta = { myCustomMetadata: string }
+
+// Notice how the custom type has now become the second argument
+uppy.on<'complete', Meta>('complete', (result) => {
+  // The passed type is now merged into the `meta` types.
+  const meta = result.successful[0].meta.myCustomMetadata
+})
+```
+
+Plugins that add their own events can merge with existing ones in `@uppy/core` with `declare module '@uppy/core' { ... }`. This is a TypeScript pattern called [module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation). For instance, when using [`@uppy/dashboard`][dashboard]:
+
+<!-- eslint-disable @typescript-eslint/no-unused-vars -->
+```ts
+uppy.on('dashboard:file-edit-state', (file) => {
+  const fileName = file.name
+})
+```
+
+## Batch pre-signing URLs for AWS S3 Multipart
+
+The [`@uppy/aws-s3-multipart`][aws-s3-multipart] plugin can be used to upload files directly to an S3 bucket using S3’s Multipart upload strategy. With this strategy, files are chopped up in parts of 5MB+ each, so they can be uploaded concurrently. It is also very reliable: if a single part fails to upload, only that 5MB chunk has to be retried.
+
+However, in Uppy 1.0, every part had to make the trip to the server to generate a pre-signed URL. This meant that a 1GB file uploaded in 5MB chunks would require two hundred trips to the server.
+
+As of Uppy 2.0.0, we now pre-sign URLs in batches. That same 1GB file now only takes fifty trips to the server (if the limit was four).
+
+This is now the new default. Thanks to [@martin-brennan](https://github.com/martin-brennan) for this contribution!
+
+Do you care about reliable uploads? You could also consider [`@uppy/tus`][tus] with a self-hosted or Transloadit Tus server. Tus can resume uploads, supports smaller chunks, and offers increased upload speeds.
+
+## And more
+
+- The `.run` method on the `Uppy` instance has been removed. This method was already obsolete and only logged a warning. As of this major version, it no longer exists.
+
+- [`@uppy/informer`][informer] now supports showing multiple notifications at the same time. The notifications themselves have also been improved.
+
+- Improved screen reader accessibility for checkboxes and the 'remove file' button for [`@uppy/dashboard`][dashboard].
+
+- Sort files and folders alphabetically in the Google Drive provider.
+
+- Polished our code base with improved eslint rules, private field methods, and other modern JavaScript features that help us write more intention-revealing and safe code.
+
+- To make Uppy more friendly towards new contributors, we have renamed our `master` branch to `main`.
+
+## What future remains for 1.0?
+
+Uppy 1.0 will continue to receive bug fixes for three more months (until <time datetime="2021-12-01">1 December 2021</time>), security fixes for one more year (until <time datetime="2022-09-01">1 September 2022</time>), but no more new features after today. Exceptions are unlikely, but _can_ be made – to accommodate those with commercial support contracts, for example.
+
+## That's it!
+
+We hope you'll waste no time in taking Uppy 2.0 out for a walk. When you do, please let us know what you thought of it on Reddit, HN, ProductHunt, or Twitter. We're howling at the moon to hear from you!
+
+<!-- definitions -->
+
+[core]: /docs/uppy/
+[dashboard]: /docs/dashboard/
+[informer]: /docs/informer/
+[aws-s3-multipart]: /docs/aws-s3-multipart/
+[tus]: /docs/tus/

+ 0 - 51
website/src/_posts/2021-XX-XX.md

@@ -1,51 +0,0 @@
----
-title: "Uppy 2.0.0: New major release"
-date: 2021-06-30 # TODO
-author: aduh95
-image: null # TODO
-published: false
----
-
-# Uppy 2.0.0
-
-## Browser support
-
-In Uppy v2.0, we've removed all built-in polyfills. If you need your app to
-support older browser (such as IE 11), you can add some polyfills to your bundle
-to make it work:
-
-- [abortcontroller-polyfill](https://github.com/mo/abortcontroller-polyfill)
-- [core-js](https://github.com/zloirock/core-js)
-- [md-gum-polyfill](https://github.com/mozdevs/mediaDevices-getUserMedia-polyfill)
-- [resize-observer-polyfill](https://github.com/que-etc/resize-observer-polyfill)
-- [whatwg-fetch](https://github.com/github/fetch)
-
-If you're using a bundler, you need import them before Uppy:
-
-```js
-import 'core-js'
-import 'whatwg-fetch'
-import 'abortcontroller-polyfill/dist/polyfill-patch-fetch'
-// Order matters: AbortController needs fetch which needs Promise (provided by core-js).
-
-import 'md-gum-polyfill'
-import ResizeObserver from 'resize-observer-polyfill'
-
-window.ResizeObserver ??= ResizeObserver
-
-export { default } from '@uppy/core'
-export * from '@uppy/core'
-```
-
-If you're using Uppy from CDN, we now provide two bundles, one for up-to-date
-browsers that do not include polyfills and use modern syntax, and one for legacy
-browsers. When migrating, be mindful about which browsers you want to support:
-
-```html
-<!-- Modern browsers (recommended) -->
-<script src="https://releases.transloadit.com/uppy/v2.0.0/uppy.min.js"></script>
-
-<!-- Legacy browsers (IE11+) -->
-<script nomodule src="https://releases.transloadit.com/uppy/v2.0.0/uppy.legacy.min.js"></script>
-<script type="module">import"https://releases.transloadit.com/uppy/v2.0.0/uppy.min.js";</script>
-```

BIN
website/src/images/blog/2.0/uppy-2-0-cover.jpg


BIN
website/src/images/blog/2.0/uppy-performance-after.gif


BIN
website/src/images/blog/2.0/uppy-performance-before.gif


BIN
website/src/images/blog/2.0/uppy-ui-with-files.jpg


+ 6 - 6
website/themes/uppy/layout/partials/blog.ejs

@@ -10,10 +10,10 @@
           </a>
       </h2>
       <ul>
-        <% 
+        <%
         var seriesTracker = {}
         var totalCnt = 0
-        site.posts.sort('date', -1).each(function (post) { 
+        site.posts.sort('date', -1).each(function (post) {
           var title = post.title
           if (post.series) {
             var title = post.series
@@ -22,7 +22,7 @@
             }
             seriesTracker[post.series]++
           }
-          
+
           if (totalCnt > 10 || seriesTracker[post.series] > 1) {
             return false
           }
@@ -39,7 +39,7 @@
 
 <div class="Content js-Content with-sidebar">
   <h1>The <%- config.title %> Blog</h1>
-  <% site.posts.sort('date', -1).each(function (post) { 
+  <% site.posts.sort('date', -1).each(function (post) {
     var title = post.title
 
     if (post.series) {
@@ -48,9 +48,9 @@
   %>
     <div class="post">
       <h2><a href="/<%- post.path %>"><%- title %></a></h2>
-      <h4><%- post.date.format('dddd MMM D[,] YYYY') %></h4>
+      <div class="post-date"><%- post.date.format('dddd MMM D[,] YYYY') %></div>
       <div><%- post.excerpt %></div>
-      <a href="/<%- post.path %>">Continue reading ...</a>
+      <div class="continue-reading"><a href="/<%- post.path %>">Continue reading ›</a></div>
     </div>
   <% }) %>
 </div>

+ 2 - 2
website/themes/uppy/layout/partials/comments.ejs

@@ -1,4 +1,4 @@
-<div id="disqus_thread"></div>
+<!-- <div id="disqus_thread"></div>
 <script>
 
 /**
@@ -18,4 +18,4 @@ s.setAttribute('data-timestamp', +new Date());
 })();
 </script>
 <noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
-                            
+                             -->

+ 21 - 17
website/themes/uppy/layout/post.ejs

@@ -24,7 +24,7 @@
         }
     %>
   <h1><%- title %></h1>
-  <h4><%- page.date.format('dddd MMM D[,] YYYY') %></h4>
+  <div class="post-date"><%- page.date.format('dddd MMM D[,] YYYY') %></div>
     <%
         if (page.series) {
             var seriesList = []
@@ -47,23 +47,27 @@
         <a class="github-button" href="https://github.com/transloadit/uppy" data-icon="octicon-star" data-size="large"
         data-show-count="true" aria-label="Star transloadit/uppy on GitHub">Star</a>
     </center>
-    <%
-        var authors = page.author
-        if (authors === (authors + '')) {
-            authors = [ page.author ]
-        }
 
-        for (var i in authors) {
-            var author = authors[i]
-    %>
-        <div class="PostAuthor">
-            <a href="https://transloadit.com/about/#<%- site.data.authors[author].id %>">
-                <img class="PostAuthor-img" src="<%- gravatar(site.data.authors[author].email, 200) %>">
-                <h4 class="PostAuthor-name"><%- site.data.authors[author].name %></h4>
-                <h5 class="PostAuthor-tagline"><%- site.data.authors[author].tagline %></h5>
-            </a>
-        </div>
-    <% } %>
+    <div class="PostAuthors">
+        <%
+            var authors = page.author
+
+            if (typeof authors === 'string') {
+                authors = [ page.author ]
+            }
+
+            for (var i in authors) {
+                var author = authors[i]
+        %>
+            <div class="PostAuthor">
+                <a class="PostAuthor-link" href="https://transloadit.com/about/#<%- site.data.authors[author].id %>">
+                    <img class="PostAuthor-img" src="<%- gravatar(site.data.authors[author].email, 200) %>">
+                    <h4 class="PostAuthor-name"><%- site.data.authors[author].name %></h4>
+                    <div class="PostAuthor-tagline"><%- site.data.authors[author].tagline %></div>
+                </a>
+            </div>
+        <% } %>
+    </div>
   <br />
     <!-- <ul class="Social">
         <li><iframe src="https://ghbtns.com/github-btn.html?user=transloadit&repo=uppy&type=watch&count=true"

+ 51 - 11
website/themes/uppy/source/css/_page.scss

@@ -366,6 +366,17 @@ table tr:last-child td {
   }
 }
 
+.post-date {
+  margin: 0.5em 0 1em;
+  color: $color-gray;
+  font-size: 0.9em;
+}
+
+.continue-reading {
+  margin-top: 1em;
+  font-weight: bold;
+}
+
 // Locale page table overrides
 
 .page-docs-locales table {
@@ -403,34 +414,63 @@ body.page-docs-locales table code {
   font-size: 75%;
 }
 
-.PostAuthor:first-of-type {
-  position: relative;
-  margin-top: 40px;
+.PostAuthors {
+  margin-top: 50px;
+
+  @media screen and (min-width: 400px) {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+  }
 }
 
-.PostAuthor:not(:first-of-type) {
+.PostAuthor {
   position: relative;
-  margin-top: 10px;
+  margin-bottom: 24px;
+
+  @media screen and (min-width: 400px) {
+    width: 100%;
+    max-width: 160px;
+  }
+
+  @media screen and (min-width: 600px) {
+    max-width: 190px;
+  }
+
+  // if there's only one author there's no need to limit the width
+  &:only-child {
+    max-width: none;
+  }
+}
+
+.PostAuthor-link {
+  display: inline-block;
 }
 
 .PostAuthor-img {
   border-radius: 50%;
-  max-width: 50px;
+  max-width: 40px;
+  width: auto;
+  height: auto;
   position: absolute;
 }
 
 .PostAuthor-name {
-  margin: 0;
+  margin: 0 0 0.1em;
   padding: 0;
-  font-size: 1.2em;
-  padding-left: 70px;
-  padding-top: 3px;
+  font-size: 1em;
+  padding-left: 50px;
+  line-height: 1.2;
+
+  .PostAuthor-link:hover & {
+    text-decoration: underline;
+  }
 }
 
 .PostAuthor-tagline {
   margin: 0;
   padding: 0;
-  padding-left: 70px;
+  padding-left: 50px;
   font-size: 0.85em;
   color: darken($color-gray, 10%);
 }