Here is the main caption. Does it appear different from other stuff?
wallacepolsom

@theartofmadeline

JVL
I'd rather be in outer space đž
h
2025 on Tumblr: Trends That Defined the Year
Sweet Seals For You, Always

izzy's playlists!
d e v o n
Not today Justin
Stranger Things

titsay
almost home

Discoholic đȘ©

Product Placement
we're not kids anymore.
noise dept.
đ©” avery cochrane đ©”

seen from United States
seen from Malaysia
seen from Nepal

seen from TĂŒrkiye

seen from United States

seen from Chile

seen from Moldova
seen from Nigeria
seen from Jordan

seen from Ukraine
seen from Netherlands
seen from Venezuela
seen from Portugal

seen from United States

seen from United States
seen from United Kingdom

seen from United States
seen from United States

seen from Maldives
seen from United States
@la-change
Here is the main caption. Does it appear different from other stuff?

Anya is live and ready to show you everything. Watch her strip, dance, and perform exclusive shows just for you. Interact in real-time and make your fantasies come true.
Free to watch âą No registration required âą HD streaming
Testing typography formatting: Here is a large title because we want to see what it looks like when it wraps
Here is an intro-secondy title
Solutions to device-agnostic design problems:
List item one
List item two with subitems:
Subitem 1
Subitem 2
Final list item
Maps: use an image at small sizes with a âview mapâ link, change to an interactive map when appropriate
Loading content, long scrolls: Donât show the less necessary information, like related products or customer reviews. Do show them automatically on desktop
Force yourself to think in components
Design the bare bones, then think in enhancements
Fluid fills: Either stretch something to fill the layout, or reveal more of something to fill the layout
Navigation: Nobody is using the huge global nav anymore. Instead, put the most important action you want to push in the bottom right (like NYTimes article suggestions.)
List item one
List item two with subitems:
Subitem 1
Subitem 2
Final list item
Sub-navigation can be an enhancement Responsive design: Content-based breakpoints (when lines get too long to read, images get too wide, etc.) Avoid device-driven breakpoints
Allow content and design to define where layout adapts
Use em-based media queries over pixel values
Viewing distance: Be aware of the assumptions youâre making based on screen size.
Sometimes you have a small screen on your phone, sometimes you have a small screen on your desktop. Screen mirroring shows mobile content at a huge size.
Image optimization:
CSS background images
Javascript replace
Image compression pre-web
It takes a lot of browser effort to resize images
RESS (Responsive web design + server side optimization)
Only sends what the client needs: media, images, etc.
One codebase, deployment and URL
Device detection only used at component level
Manage performance
Perceived performanceâpretend like an action happened before it actually did)
Client-side enhancements
Service-side components
Promises and Error Handling
Weâve standardized on using promises to manage most of the asynchronous interactions in our JavaScript codebase. In addition to really enjoying the extra expressiveness and aggregation possibilities offered by promises, weâre benefitting greatly from richer error handling. Weâve had some surprises though, and this post explains some things which caught us out and some guidelines weâre following to avoid similar situations in future.
We use the excellent when.js library, however this post should be relevant no matter what implementation youâre using.
We use the excellent when.js library, however this post should be relevant no matter what implementation youâre using.
1. Reject what you would normally throw
Rejecting a promise should be synonymous with throwing an exception in synchronous code, according to Domenic Denicola. Weâve previously built some promise interfaces which can reject with no value, or with a string error message. Rejecting with a string should be discouraged for the very same reasons as throwing strings. An empty rejection is the async equivalent of throw undefined, which (hopefully) nobody would consider doing.
These practices cause us additional problems with the likes of Express and Mocha since they expect theirnext and done callbacks to be invoked with an Error instance to trigger the failure flow, otherwise they consider it a successful operation. We, quite reasonably, are in the habit of chaining promise rejections straight onto these (e.g. .then(onResolve, next) or .catch(next)). If the promise rejects with no arguments then it wonât signal a failure when used like this!Â
Guideline 1: Always reject promises with an Error instance. Do not reject with no arguments. Do not reject with non-Error objects, or primitive values.
Itâs easy to feel like rejecting a promise is less âsevereâ than throwing an exception, this impulse can lead to promises being rejected where normally you would not throw an exception (e.g. validation checks). If the above guideline is difficult to follow in a specific case because you canât think of an appropriate Error type to reject with, then perhaps it shouldnât be a rejection after all.
2. Be careful with .catch
We have been caught out with catch (a.k.a. otherwise/fail for non-ES5 browsers). The documentation for it might lead you to think that somePromise.then(onResolve).catch(onReject) is equivalent tosomePromise.then(onResolve, onReject). This appears true at a first glance:
function onResolve(result) { console.log('OK', result) } function onReject(err) { console.log('NOT OK', err.stack) } // This: somePromise.then(onResolve, onReject) // Is equivalent to: somePromise.then(onResolve).catch(onReject)
They differ, however, in how they respond to errors thrown in callbacks. If onResolve throws and we are using the .then(onResolve, onReject) form, onReject is not invoked, and the outer promise is rejected.
var outer = resolvedPromise.then(function onResolve(result) { throw new Error('this is an error') }, function onReject(err) { // Never gets invoked }) // outer is rejected with the 'this is an error' error
If onResolve throws and we are using the .then(onResolve).catch(onReject) form, onReject is invoked, and the outer promise is resolved.
var outer = resolvedPromise.then(function onResolve(result) { throw new Error('this is an error') }).catch(function onReject(err) { console.log('FAILED', err) // => FAILED [Error: this is an error] }) // outer is resolved
So, throwing inside either handler will reject the outer promise with the thrown error.
Guideline 2: Anticipate failures in your handlers. Consider whether your rejection handler should be invoked by failures in the resolution handler, or if there should be different behavior.
3. Terminating the promise chain
Guideline 3: Either return your promise to someone else, or if the chain ends with you, call done to terminate it. (from the Q docs)
This has bitten us many times when using promises in Express handlers, and resulted in hard-to-debug hung requests. This handler will never respond to the user:
function handler(req, res, next) { service.doSomething().then(function onResolve(result) { throw new Error('this is an error') res.json({result: 'ok'}) }, function onReject(err) { res.json({err: err.message}) // Never gets invoked }) }
Changing the then in the above code to done means that there will be no outer promise returned from this, and the error will result in an asynchronous uncaught exception, which will bring down the Node process. In theory this makes it unlikely that any such problem would make it into production, given how loudly and clearly it would fail during development and testing.
An alternative is to add a final .catch(next) to the promise chain to ensure that any error thrown in either handler will invoke the Express error handler:
function handler(req, res, next) { service.doSomething().then(function onResolve(result) { throw new Error('this is an error') res.json({result: 'ok'}) }, function onReject(err) { res.json({err: err.message}) // Never gets invoked }) .catch(next) // next is invoked with the 'this is an error' error }
This goes against the above guideline, since we are creating a new promise rather than ending the promise chain. You could argue that we trust next not to throw and as such there is no chance for the outer promise to reject, in addition there is no possibility of a hanging request or an uncaught failure (unless you throw undefined in either handler!).
If the idea of done bringing down a Node process makes you uncomfortable enough to ignore this âgolden ruleâ of promise error-handling, then perhaps this is a good option. The important thing is that errors do not hang requests, or get quietly transformed into successes.
Summary
Weâve seen 3 simple guidelines which should feel familiar if youâve dealt with synchronous exception handling best practices. In fact they could almost be generalized to apply to both sync and async exception handling:
Throw meaningful errors (and a string is not an error)
Be aware of downstream effects of errors on the flow of execution
Bubble exceptions up to a top level handler
This is a powerful feature of promises - letting us deal with errors in a stye that is more natural to us, as long as we are actually mindful of this, and remember to follow similar rules.
Jon Merrifield Engineer
Choosing Charlie
How casual user interviews affected our petition creation design concept
In the past year, I have been designing a new petition creation and management experience on Change.org. For a site that specializes in viral world-changing petitions, this is an important process to perfect. Our previous petition creation experience consisted of three inputs and a giant red button(Fig. 1)Â While the design optimized the quantity of petitions created, it hardly addressed quality. Our product team was willing to compromise a portion of our creation rates in exchange for better-written content, thereby leading to more petition victories. Our specific metrics to gauge this projectâs success include petition author sharing rates and the percent of petitions that reach five or more signatures.
Fig. 1: Previous petition creation page Our previous petition creation concept optimized for quantity of petitions over quality. We had lots of room for improvement.
Designing the concepts
After far-reaching research and generative design exploration, our design team narrowed our ideas down to three concepts for the new creation process. âAlpha,â the most forward-looking design, was a blank worksheet with subtle placeholder text. The minimal template featured in-line editing and sleek interactions (Fig. 2). Alpha was fast and innovative, but we questioned whether our less tech-savvy users would agree.
Fig. 2: Alpha concept This Balsamiq mock-up of Alpha shows interaction details like tips that appear upon input focus, hover states, and exclusively in-line editing.
Our second concept, âBravo,â expanded on Alpha. The initial page used in-line editing, but subsequent steps offered more guidance. A wizard walked users through four steps, a quality indicator measured petition completeness, and a preview button showed the content exactly as it would appear on the site. Bravo was bulkier than Alpha, but we hoped its thoroughness would translate into high-quality petitions.
âCharlieâ borrowed the same comprehensive steps from Bravo, but the initial view was more similar to our preexisting petition creation page. In lieu of Alpha and Bravoâs in-line editing, Charlie used standard inputs, allowing the help text to show permanently (Fig. 3). As a creator filled out the fields, a miniature petition preview built in real-time in the right column. Even so, our design team considered Charlie a fall-back concept because it was the most traditional.
Fig. 3: Charlie concept Charlie focuses attention on one field at a time. A miniature petition preview builds in real time as the creator fills out the fields.
Product Developer Alexander McCormmach and our engineers built these three concepts as interactive prototypes using meteor.js. They werenât connected to any backend, but they offered an easy way to user-test the products. Armed with laptops, we were ready to hit the streets.
Conducting the interviews
Alexander and I, along with a rotating group of engineers, walked to coffee shops near our Potrero Hill office to conduct the user interviews. We offered each participant a gift certificate to the coffee shop and promised to take less than an hour of their time.
Since most creators have an issue in mind before composing a petition, we started by helping our interviewees brainstorm an appropriate issue. Their ideas ranged from expanding late-night MUNI service to abolishing San Franciscoâs open container law. We used the screen recording app Silverbackto record each interview. We initially only tested Alpha and Bravo, confident weâd have success with one or the other. After only three tests with Alpha, it was obvious that new petition creators needed more guidance. Bravoâs results were a bit more promising, but some people didnât click into the fields until they had fully formed their phrasing, eliminating the value of the tips on focus. We decided to cut Alpha from the tests and concentrate exclusively on Bravo and Charlie. With Charlie, our testers wrote significantly higher quality petitions and better understood the process as a whole. The highlighted inputs eliminated confusion of where to focus. Interviewees were more likely to read the help text and frequently delighted in the real-time preview on the right. Charlie was our clear winnerâan outcome we might not have chosen without user interviews.
Parsing the results
Excited by our research findings, our design team was confident enough to move forward with Charlie. We scaled the design to accommodate the MVP, and our team, the Candy Rats*, built the page on our new node.js platform (Fig. 4). The first Charlie creation page is now part of an experiment on Change.org, and we are collecting initial data. Both qualitative user-interviews and quantitative experiment data will inform the next design iteration. Our trek isnât over, but feels good to have found the candy mountain.
* If you donât understand this name, rewatch this video.
Fig. 4: New Charlie petition creation This page is part of an experiment to see how the new design performs. Weâre currently building the next steps of the creation flow, including editing and previewing the petition before sharing it with friends.
Lauren P. Adams Product Designer lauren at change dot org @laurenpadams
Project contributors: Luke Arduino, Alain Bloch, José Capó, Warren Colbert, Dustin Diaz, Ali Faiz, Nate Kerksick, Alexander McCormmach, Jon Merrifield, Mike Nothum, and Jeremy Thibeaux