Giter Site home page Giter Site logo

altis-seo's Introduction

Altis SEO

Search Engine Optimisation module for Altis.

Packagist Version

SEO

Take control of your search engine and social media listings through meta data and integrations with plugins such as AMP and Facebook Instant Articles.

altis-seo's People

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

altis-seo's Issues

Task: Add Yoast SEO (free) to SEO module

We want to include Yoast SEO as our de facto SEO solution. By default, we will include WordPress SEO (free) but we will need to support premium versions as well.

Acceptance Criteria:

  • Remove humanmade/wp-seo from composer.json
  • Add yoast/wordpress-seo to composer.json
  • Document how to get and use premium version (eg add Composer repo, add license key, etc)

Add config support for AMP

The AMP plugin has a couple of primary settings, notably

  • mode: whether to use native, paired or classic mode for its output
  • automatically accept sanitization for any newly encountered AMP validation errors.
  • disable admin bar on AMP pages
  • serve all pages as AMP or select by post type & page template

We could potentially hide the AMP admin pages however the validated URLs and error pages are potentially useful so this isn't quite as straightforward as hiding everything. The validations page could be made into the primary one.

Analytics could be added via config as it's just an array of JSON objects.

Migrate command processes every post object on the site

While upgrading to Altis v9, running altis migrate in a local environment with a recently refreshed database from an Altis project with approximately 19k published posts caused 361,017 post objects to be processed while "Migrating legacy SEO settings" and took almost 2 hours to run to completion. The query within the migrate_wpseo_to_yoast function is returning every single post object on the site, including revisions and oembed caches.

The site in question has been using Yoast Premium for years, and never relied on the legacy Altis SEO module. If I query the database directly, I see no meta keys matching our target $old values in the meta_mapping object. e.g.,

select count(*) from wp_postmeta where meta_key="_meta_title";
+----------+
| count(*) |
+----------+
|        0 |
+----------+

The reason things are taking so long to run is the meta_query we execute within that migrate_wpseo_to_yoast() function:

	'meta_query' => [
		'relation' => 'OR',
		[
			'meta_key' => '_meta_title',
			'meta_compare' => 'EXISTS',
		],
		[
			'meta_key' => '_meta_description',
			'meta_compare' => 'EXISTS',
		],
		[
			'meta_key' => '_meta_keywords',
			'meta_compare' => 'EXISTS',
		],
	],

Note that we're using meta_key and meta_compare in the nested arrays. If I am reading the wp meta query docs properly, those keys are only accepted in the meta_query array itself, and nested arrays should be using key and compare, e.g.

	[
		'key' => '_meta_keywords',
		'compare' => 'EXISTS',
	],

Because of this difference between the expected arguments and what we are providing, I believe the current query is stripping all meta query comparisons and returning every post on every site. This will result in a LOT of unnecessary processing while upgrading to Altis 9, because the migration routine then goes through the entire site and tries to find each of those three keys on every single post object (none of which have them), to no actual benefit.

You can validate that the meta_key is being ignored and returning all posts by trying this query locally in wp shell:

// Handle post meta.
$posts_query_args = [
    'post_type' => 'post',
    'post_status' => 'any',
    'posts_per_page' => 0,
    'fields' => 'ids',
    'suppress_filters' => true,
    'meta_query' => [
        'relation' => 'OR',
        [
            'meta_key' => '2014 SUBARU IMPREZA WRX Hatch, Premium trim, 32k miles',
            'meta_compare' => 'EXISTS',
        ],
    ],
];
$posts = new WP_Query( $posts_query_args );

// Will log however many posts you have.
WP_CLI::log( $posts->found_posts );

Switching these meta parameters to key and compare may not fix the issue, because I made those changes locally and started running that query 30 minutes ago and it still hasn't completed. A triple-barrel EXISTS meta_compare on a medium-to-large site appears to be paralyzingly slow. (In my testing, only querying for a single EXISTS meta query at a time was any kind of fast.) However, I suspect that if it does complete, it will correctly detect that no posts in the site actually have these keys present.

This is a pretty big issue with the Altis 9 upgrade path.

Acceptance criteria:

  • Only posts with old WP SEO meta keys present get processed when running the Altis SEO migration routine.
  • The migration command runs in a reasonable amount of time

Add built in Image sitemap

Now that we have the core WP sitemaps we can build on that value by providing custom sitemaps for specific use cases. Depending on the complexity and variation in requirements the outcome of this might be one of:

  • Documentation for building an image sitemap
  • An image site map provider class with appropriate hooks / filters documented

The docs for how to construct one are here:

https://support.google.com/webmasters/answer/178636?hl=en

We can either parse post content and add image details inline eg:

<url>
    <loc>http://example.com/sample.html</loc>
    <image:image>
      <image:loc>http://example.com/image.jpg</image:loc>
    </image:image>
    <image:image>
      <image:loc>http://example.com/photo.jpg</image:loc>
    </image:image>
</url>

Or it can be a pure image sitemap.

Other metadata can be specified such as caption etc which can be read from gutenberg blocks, the HTML structure or the attachment post data.

Task: Remove/Hide Yoast upsells

We assume many of our clients are already aware of what Yoast is and don't need to be reminded about upgrading. For those that don't, that can be addressed by AMs/PMs/Sales. For a better user experience and a more native-looking UI, we want to remove the upsell banners.

Acceptance Criteria:

  • remove upsell banners and sidebar in settings pages
  • remove upsells in edit post screen
  • Remove Yoast icon in social meta screen

Task: Create WP SEO -> Yoast SEO migration scripts

Migrate any existing WP SEO data into Yoast SEO. This includes both global settings and per-post meta.

Acceptance Criteria:

  • Create a migration script hooked to wp altis migrate (e.g. wp altis migrate seo)
    • Migrate WP SEO option data
    • Migrate WP SEO postmeta

Dev Notes

how does WP SEO store the custom title, description and keywords for a post? How can those be migrated?

Both WP SEO and Yoast SEO store that information in post meta, and each have comparable meta keys for that data. Yoast uses a formula for the SEO title (e.g. %%title%% %%page%% %%sep%% %%sitename%%). We could either default to this or migrate existing data in the WP SEO _meta_title field to _yoast_wpseo_title, which is the Yoast equivalent and only added if the title was manually changed from the default pattern.

Screen Shot 2021-05-10 at 12 37 13 PM

Probably what makes the most sense is to migrate the meta data over if it exists -- WP SEO also has default patterns which would get migrated over as part of the migration process, so the most important information here is the meta descriptions and SEO keywords, which would be unique for each post.

XML Sitemap additions

By default all URLs are treated the same, eg. same frequency for updates, priority etc.

This is pretty suboptimal so I think we can make a companion plugin to provide some better defaults out of the box including easier support for news, video and image sitemaps.

  • override default priority and frequency for home page, archive pages, posts and pages with sensible defaults
  • provide hooks for easily setting the above, along with configuration options
  • provide mechanism to split sitemaps by post type
  • provide easy to use hooks to provide image, news and video data as arrays and handle SImpleXMLElement generation

Document Yoast SEO Premium authentication keys

Yoast's instructions inform people to use the following commands:

composer config -g http-basic.my.yoast.com token ...
composer config repositories.my-yoast composer https://my.yoast.com/packages/
composer require yoast/wordpress-seo-premium

To create the auth.json file though, you need to run the first command without -g; additionally, these commands should be run to generate the files, and not just added to the build script.

Documentation does not mention URL parameter behaviour

From #173

We need to document how redirects handle URL parameters (ie passthrough, can't use them in the source (iirc)).

Additionally, it would be good to document how this interacts with analytics; specifically, that redirects are not tracked in analytics (as they're performed server-side), and tracking parameters/data like UTM parameters will be passed through to the destination.

Yoast Premium installation broken with Altis v8

Yoast premium when installed via composer adds the free version of Yoast SEO as a dependency and attempts to load it from the same directory / location.

We need to determine how best to load Yoast SEO Premium alongside our modifications for the installation location and loading for the Yoast SEO free version.

Content on atis.cloud subdomains is being indexed by Google

Google has indexed more than 2,500 pages of content on *.altis.cloud sub domains

  • The subdomains reveal the relationship between Altis/HumanMade and our clients as they all seem to have clients names in them. I understand some clients don't allow us to publicise their names as clients, this may go against that understanding.
  • Some of these pages are not redirecting to their proper domains (e.g. plan-international-prod and webedia-arabia-prod)
  • Some of the pages indexed are staging environments (e.g tokai-staging)
  • This is bad for our clients SEO having duplicate content on these different domains.
  • Some staging environment content is accessible without basic auth. e.g. https://tokai-staging.altis.cloud/uploads/2021/02/PreEnrollmentStudyGuide.pdf

Other search engines need to be checked. Yep,
bing - https://www.bing.com/search?q=site%3Aaltis.cloud
duckduckgo - https://duckduckgo.com/?q=site%3Aaltis.cloud

Acceptance criteria:

  • Fix require-login plugin to allow robots.txt here.
  • Update documentation on basic-auth to indicate it only works for PHP files, and that custom nginx rules can be used for anything else.

Add config support for FBIA

FBIA has a whole page of settings on its wizard that could be defined via the JSON config. We need to look at all the possible forms this data can take, document those here and then implement the necessary filters.

Replace existing SEO tools with Yoast SEO

For Altis v8 and moving forward, we will be using Yoast SEO as our SEO solution by default. There are many parts to this integration including UI changes to remove/hide upsell banners and re-theme to Altis branding, migrating settings and data from WP SEO to Yoast SEO and removing/replacing Altis SEO features with their equivalents that are built into Yoast.

Action Items

  • We need a way to disable/remove/hide the "Add related keyphrase" accordion menu in the edit post metabox

[Sidebar]
Screen Shot 2021-05-12 at 12 19 35 PM

[Post Meta]
Screen Shot 2021-05-12 at 12 19 50 PM

  • We need one of the randomized (Styled Components) classnames to have a static class
    .yoast span.yTsQm -- the wrapper around the styled search result shortcode element
    Screen Shot 2021-05-12 at 12 35 12 PM

Task: Remove Yoast admin UI elements

There are additional admin UI elements that should be removed to simplify the admin.

Acceptance Criteria:

  • Hide the "Add related keyphrase" accordion option if not premium (this may need to be done on their end)
  • remove Premium submenu page
  • Remove admin bar menu
  • If on non-production environments, ignore search engines discouraged notice ("huge SEO issue")
  • remove helpscout beacon

Dev notes
example code:

add_action( 'admin_init', function() {
	$options = get_option( 'wpseo' );

	// Disable the admin bar menu.
	$options['enable_admin_bar_menu'] = false;
	if ( in_array( Altis\get_environment_type(), [ 'local', 'development' ], true ) ) {
		// Don't display the HUGE SEO issue on local or dev environments.
		$options['ignore_search_engines_discouraged_notice'] = true;
	}
	update_option( 'wpseo', $options );

	// Remove Helpscout.
	add_filter( 'wpseo_helpscout_show_beacon', '__return_false' );

	// Remove the Premium submenu.
	$menu = 'wpseo_dashboard';
	$submenu = 'wpseo_licenses';
	remove_submenu_page( $menu, $submenu );
}, 11 );

Yoast SEO warning is displayed on dev/staging sites

Steps to reproduce:

  1. Update to latest SEO with Yoast included
  2. Open admin
  3. Observe warning about site being hidden from search

This warning isn't useful for development and staging environments, and hence should not be shown.

We may want to remove this warning entirely, as private sites will likely also receive it.

Screen Shot 2021-06-10 at 16 16 47

Acceptance criteria:

  • Hide SEO warning on development and staging environments

Yoast social images use all attachments with `filesize`

When building the index for attachments (and quite possibly in other areas too), Yoast will filter the attachment image sizes (by default full, large, medium_large) by filesize (see https://github.com/Yoast/wordpress-seo/blob/trunk/inc/class-wpseo-image-utils.php#L340) to exclude anything that is over 2MB.

In conjunction with S3 uploads, this makes these functions very slow, as each image has to be pulled from S3 in full to check it's file size.

Rather than using the image sizes at all, in Altis it seems we should just be passing a tachyon image somehow and not going any of those checks etc.

Task: Remove Yoast dashboard widget

By default, Yoast SEO adds a dashboard widget (which includes custom CSS and JS). We don't need this in the Altis Dashboard so we can remove.

Acceptance Criteria:

  • Remove Dashboard widget
    • dequeue dashboard scripts
    • dequeue dashboard styles

Dev Notes
example code:

	add_action( 'admin_init', function() {
		// Remove the Dashboard widget.
		remove_meta_box( 'wpseo-dashboard-overview', 'dashboard', 'normal' );
		wp_dequeue_script( 'dashboard-widget' );
		wp_dequeue_style( 'wp-dashboard' );
	}

Task: Update Yoast styles to match Altis branding

Update the Yoast CSS to match Altis branding. This may be easiest by overriding their :root variables.

:root {
    --yoast-border-default: 1px solid rgba(0,0,0,0.2);
    --yoast-color-default: #404040;
    --yoast-color-default-darker: #303030;
    --yoast-color-primary: #a4286a;
    --yoast-color-secondary: #f7f7f7;
    --yoast-color-white: #fff;
    --yoast-color-green: #6ea029;
    --yoast-color-primary-darker: #7b1e50;
    --yoast-color-primary-lighter: #f5d6e6;
    --yoast-color-secondary-darker: #d9d9d9;
    --yoast-color-button-upsell: #fec228;
    --yoast-color-button-upsell-hover: #f2ae01;
    --yoast-color-dark: #303030;
    --yoast-color-sale: #fec228;
    --yoast-color-sale-darker: #feb601;
    --yoast-color-border: rgba(0,0,0,0.2);
    --yoast-color-label: #303030;
    --yoast-color-label-help: #707070;
    --yoast-color-active: #6ea029;
    --yoast-color-inactive: #dc3232;
    --yoast-color-inactive-text: #707070;
    --yoast-color-inactive-grey: #9e9e9e;
    --yoast-color-inactive-grey-light: #f1f1f1;
    --yoast-color-active-light: #b6cf94;
    --yoast-transition-default: all 150ms ease-out;
    --yoast-color-link: #006dac;
    --yoast-color-border--default: rgba(0,0,0,0.2);
    --yoast-color-focus: 0 0 0 2px #007fff,0 0 0 5px #bfdfff;

Acceptance Criteria:

  • update colors (swap Yoast purple for Altis blue)
  • edit or change the Schema svg icon on edit post page (wrong color, presumably part of the actual svg file)
  • override the Yoast Wizard styles

Dev Notes

  • PoC style overrides are here
  • The Wizard styles need to be loaded separately because they bail out of WP before any other styles can be loaded. Luckily, they have an action that can be hooked into to add our own styles.

`og:article:author` opengraph property should be `article:author`

In https://github.com/humanmade/altis-seo/blob/master/inc/opengraph/class-author-presenter.php we add the og:article:author tag, however from what I can see, this is not an opengraph tag. Looking at https://stackoverflow.com/questions/29748013/what-is-the-correct-implementation-of-the-open-graph-article-type, this should be just article:author. Also, check the same when using this tool: https://webcode.tools/generators/open-graph/article

Spike: Research Yoast SEO for Integration with Altis OOB

Before diving into integrating Yoast SEO with Altis, we'd like to deep dive into some considerations about the plugin as to:

  • What License should this be? Free?
  • Dependabot to track relevant updates?
  • How does this affect documentation and maintain it?
  • To find what are out of place things in UI, upsells, etc. that we might want to get rid of.

The outcome of this spike will determine the next steps for this feature.

Timebox: 2 days

Setting YOAST_ENVIRONMENT development breaks scripts loading

Steps to reproduce:

  1. Install yoast seo premium
  2. Load post list screen in admin
  3. Check browser console warnings

brokenyoastscripts

Bug caused by 'enable_yoast_development_mode', which causes yoast seo to try to load non-minified scripts, which are not bundled with yoast seo premium.

Document solution for google site verification

More info here https://support.google.com/webmasters/answer/9008080?hl=en

I think the recommended options are the ones that don't rely on PHP running or any other aspect of the system other than the webserver which leaves:

HTML file upload

The file name is unique each time so not really something that can be accounted for in config, easier just to let the webserver do it's thing and document where to put it.

DNS record

This option we could possibly support as part of a build step by provisioning Route53 updates but I have a feeling that'd require the domain to be transferred to and managed by us.

Yoast database migration issues

It seems in some cases when upgrading the Yoast SEO version some database migrations are not happening. Need to explore why this can occur in our setup and resolve it.

Task: Remove humanmade/meta-tags

The humanmade/meta-tags plugin can be replaced by Yoast entirely. The only thing that the Meta Tags plugin supports that is not supported out of the box in the free version of Yoast is og:article:author and og:article:section. To maintain backwards compatibility, these two tags could be added/ported over manually in our integration code. On the other hand, Yoast adds a lot more data including the ability to have unique meta tags for different social networks (twitter vs facebook).

Screenshots

humanmade/meta-tags

Screen Shot 2021-05-10 at 12 43 30 PM

yoast/wordpress-seo

Screen Shot 2021-05-10 at 12 51 05 PM

Acceptance Criteria:

  • Remove humanmade/meta-tags from composer.json
  • Add og:article:author and og:article:section tags to appropriate post headers

Yoast is not indexed on new site / migration

When the new site is created, we index ES, but we don't index Yoast. The same goes for the migration command I think. We should at least index yoast when people are upgrading to an Altis version with Yoast I think?

Filter meta tags thumbnail sizes based on platform

In addition to setting appropriate image sizes for each platform using tachyon we should filter the default thumbnail size passed down through all contexts to something more reasonable than the full original image size.

  • Twitter: 1200x600 (2:1) crop
  • Opengraph: 1200x627 (16:9) no crop
  • Default: 1200x1200 no crop

To do this use the following filter from the meta-tags plugin:

https://github.com/humanmade/meta-tags/blob/master/inc/namespace.php#L255

So for each type you would use the filters:

  • hm.metatags.context.default
  • hm.metatags.context.opengraph
  • hm.metatags.context.twitter

Then check for the existence of $meta['image] and do the following:

  • check if it's already a tachyon image (string match against the TACHYON_URL constant)
    • if yes then use add_query_arg() to set the resize (crop) or fit (no crop) parameter to that listed above
    • if no then use tachyon_url() and pass the resize or fit parameter in the 2nd parameter of that function

Acceptance criteria:

  • Filters are not applied if tachyon is disabled in config
  • Opengraph meta tag og:image contains a tachyon URL set to 1200x627 with no cropping
  • Twitter card meta tag twitter:image contains a tachyon URL set to 1200x600 with cropping
  • calling HM\MetaTags\get_meta_for_context() with no arguments returns an array with a key image that is a tachyon URL with constraining dimensions of 1200x1200 with no cropping

To test the above upload a large featured image to a post and check the meta tags output in the <head> on the post page.

Preserve URL parameters

Issue

Currently the HM Redirects plugin doesn't support URL's with parameters. So when setting up a redirect, the user can only set old-url to redirect to new-url.

So when the complete url https://dev.altis.dev/old-url/?utm_content=search contains a parameter it won't redirect accordingly and it will cause a 404 error.

Accepted Criteria

Ensure that when a url contains a parameter, that the parameters are preserved in the redirect.
EG: https://dev.altis.dev/old-url/?utm_content=search redirects to https://dev.altis.dev/new-url/?utm_content=search

GitHub

Current Issue humanmade/hm-redirects#33
Pull request humanmade/hm-redirects#55

Allow installing facebook-instant-articles-wp with stable minimum-stability flag?

I'm not 100% sure where the best place to raise this issue is. Trying to install altis without a "minimum-stability": "RC" specified, I get errors when composer tries to install altis/seo:

➜  platform-site git:(upgrade-altis-to-1.0.0) ✗ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Removal request for altis/seo == 1.0.0.0-RC3
    - Conclusion: remove humanmade/facebook-instant-articles-wp 4.2.1+hm
    - Removal request for altis/altis == 1.0.0.0-RC3
    - Installation request for altis/altis ^1.0 -> satisfiable by altis/altis[1.0.0, 1.0.0-rc3].
    - altis/altis 1.0.0 requires altis/seo ^1.0.0 -> satisfiable by altis/seo[1.0.0-rc3, 1.0.0, 1.0.1].
    - altis/seo 1.0.1 requires humanmade/facebook-instant-articles-wp 4.2.1+hm -> satisfiable by humanmade/facebook-instant-articles-wp[4.2.1+hm].
    - altis/seo 1.0.0 requires humanmade/facebook-instant-articles-wp 4.2.1+hm -> satisfiable by humanmade/facebook-instant-articles-wp[4.2.1+hm].
    - Conclusion: don't install humanmade/facebook-instant-articles-wp 4.2.1+hm|keep humanmade/facebook-instant-articles-wp 4.2.1+hm

I think this may be a result of the version number "4.2.1+hm" in https://github.com/humanmade/facebook-instant-articles-wp not being considered a stable release. Do you have any ideas how to change this so that we can install Altis 1.0.0 final without the RC stability flag?

Documentation linting issues

This ticket addresses the issues raised by the documentation linting

composer dev-tools lintdocs all -l packages/seo

File Issues:

None

Markdown issues:

Linting: 6 file(s)
Summary: 181 error(s)

Style issues:

11 errors, 0 warnings and 0 suggestions in 6 files.

Yoast SEO not translating correctly

Yoast SEO isn't being completely translated as expected.

Steps to reproduce:

  1. Ensure the SEO Altis Module is activated.
  2. Login to the site's wp-admin area.
  3. Ensure a non English language is used for the user, this is enabled in the user profile - For this issue I used Japanese.
  4. Navigate to edit or create a new post.
  5. Observe the Yoast SEO meta section, sidebar navigation and side bar panel all show in the default language and not language select to the user.

Expectations

While the Yoast SEO plugin isn't 100% translated in Japanese, we should still be seeing these fields translated. Looking up some of the strings in the Locale translation for the plugin, they have been translated already. https://translate.wordpress.org/locale/ja/default/wp-plugins/wordpress-seo/

Screenshots

Current screenshots:

Screenshot 2022-10-07 at 10 37 35

Screenshot 2022-10-07 at 11 08 20

Would expect to see more translations in place:

Screenshot 2022-10-07 at 11 07 09

Screenshot 2022-10-07 at 11 08 32

Acceptance criteria:

  • see more translated strings

Slow meta-value query in Yoast sitemaps

Currently Yoast sitemaps does a pretty gnarly query for author sitemaps. This is because the sitemap also has to exclude anyone with the wpseo_noindex_author == on user-meta; what's more it's also ordering by another meta key value _yoast_wpseo_profile_updated.

On large sites this query can take more than 160 seconds.

Fix Yoast's author sitemap generation with guest-authors

Yoast authors sitemap generation queries authors, optionally with published posts, so with Authorship activated, the sitemaps do not generate the desired outcome.

We need to detect for activation of both features, and replace the author sitemaps provider with an extended version of the class that accounts for guest authors as well.

Task: Remove XML Sitemaps & update robots.txt

XML Sitemaps are provided by Yoast SEO, which overrides our sitemaps. Removing our XML sitemap code works as expected, however the robots.txt file still links to the old XML sitemap. It needs to link to the sitemap at sitemap_index.xml

Acceptance Criteria

  • XML Sitemaps options are removed from load.php
  • XML Sitemaps documentation is updated to note that sitemaps are provided by Yoast
  • Robots.txt functionality that adds sitemap link is updated to point to new sitemap location.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.