diff --git a/docs/framework/angular/changeset.md b/docs/framework/angular/changeset.md
new file mode 100644
index 00000000..7307a7d4
--- /dev/null
+++ b/docs/framework/angular/changeset.md
@@ -0,0 +1,44 @@
+# AngularJS: Changesets
+
+!!! caution "Work in progress"
+
+ This documentation is still a work in progress.
+
+The [Quick Start](quickstart.md) and [Loader](loader.md) provide examples of
+creating *new* screens. But what if you need to alter an *existing* screen?
+CiviCRM allows third-parties to define *changesets* which programmatically
+manipulate Angular content before sending it to the client.
+
+## Background
+
+Most AngularJS tutorials focus on idealized projects where a single
+developer or product-owner exercises full authority over their application.
+But CiviCRM is an _ecosystem_ with a range of stakeholders, including many
+developers (authoring indpendent extensions) and administrators (managing
+independent deployments with independent configurations).
+
+...
+
+## tldr
+
+```php
+function mailwords_civicrm_alterAngular(\Civi\Angular\Manager $angular) {
+ $changeSet = \Civi\Angular\ChangeSet::create('inject_mailwords')
+ // ->requires('crmMailing', 'mailwords')
+ ->alterHtml('~/crmMailing/BlockSummary.html',
+ function (phpQueryObject $doc) {
+ $doc->find('.crm-group')->append('
+
+
+
+ ');
+ });
+ $angular->add($changeSet);
+}
+```
+
+```
+cv ang:html:list
+cv ang:html:show
+cv ang:html:show --diff
+```
diff --git a/docs/framework/angular/files.md b/docs/framework/angular/files.md
new file mode 100644
index 00000000..f7acd243
--- /dev/null
+++ b/docs/framework/angular/files.md
@@ -0,0 +1,73 @@
+# AngularJS: File names
+
+As a developer working with CiviCRM-Angular, you write *Angular modules* --
+these modules are composed of various JS/CSS/HTML files which define the
+*services*, *directives*, *controllers*, and *routes*.
+
+For sake of predictability, these files are placed in the `ang/` folder, and
+they follow a naming convention.
+
+!!! note "How does this work with `civix`?"
+ When you generate Angular code via `civix`, the files are
+ named according to convention.
+
+ One file, `ang/{mymodule}.ang.php`, provides instructions for the
+ file-loader. It lists any files which match the naming
+ convention.
+
+!!! note "What if I don't use `civix`? What if my code doesn't follow the naming convention?"
+ The file-loader needs some information about the name and location of
+ your AngularJS code, but you don't need to follow the convention. You
+ can configure it via hook. See: [AngularJS: Loading](/framework/angular/loader.md).
+
+## Abridged convention
+
+The abridged convention applies to small Angular modules with a narrow
+purpose -- such as defining a singular `service` or `directive`. These
+modules only have 2 or 3 files.
+
+ * `ang/{mymodule}.ang.php` - General metadata about the module (per [hook_civicrm_angularModules](/hooks/hook_civicrm_angularModules.md)).
+ * `ang/{mymodule}.js` - All Javascript for the module.
+ * `ang/{mymodule}.css` - All CSS for the module (if applicable).
+ * `ang/{mymodule}.md` - Developer documentation about the module (if applicable).
+
+## Full convention
+
+The full convention applies to bigger Angular modules which serve a broader
+purpose -- such as defining a new screen with a series of related
+`directive`s, `controller`s, and `service`s. Each of these elements may
+have multiple aspects (JS/HTML/CSS).
+
+__Module Files__
+
+ * `ang/{mymodule}.ang.php` - General metadata about the module (per [hook_civicrm_angularModules](/hooks/hook_civicrm_angularModules.md)).
+ * `ang/{mymodule}.js` - General metadata about the module.
+ * `ang/{mymodule}.css` - General CSS that applies throughout the module (if applicable).
+ * `ang/{mymodule}.md` - Developer documentation about the module (if applicable).
+
+__Directive Files__
+
+ * `ang/{mymodule}/{FooBar}.js` - The declaration and logic for a directive named `mymoduleFooBar` or `
`.
+ * `ang/{mymodule}/{FooBar}.html` - The main/default template for the directive (if applicable).
+ * `ang/{mymodule}/{FooBar}/{Extra}.html` - If you have multiple templates used by the same directive (e.g. via `ng-include` or conditional logic), then put them in a subdir.
+ * `ang/{mymodule}/{FooBar}.css` - Any CSS specifically intended for `mymoduleFooBar` (if applicable).
+ * `ang/{mymodule}/{FooBar}.md` - Developer documentation about the directive (if applicable).
+
+__Controller Files__ (These follow the same convention as directives, but they have the suffix `Ctrl`.)
+
+ * `ang/{mymodule}/{FooBar}Ctrl.js` - The declaration and logic for a controller named `MymoduleFooBarCtrl`.
+ * `ang/{mymodule}/{FooBar}Ctrl.html` - The main/default template for the controller (if applicable).
+ * `ang/{mymodule}/{FooBar}Ctrl/{Extra}.html` - If you have multiple templates used with the same controller (e.g. via `ng-include` or conditional logic), then put them in a subdir.
+ * `ang/{mymodule}/{FooBar}Ctrl.css` - Any CSS specifically intended for `MymoduleFooBarCtrl` (if applicable).
+ * `ang/{mymodule}/{FooBar}Ctrl.md` - Developer documentation about the controller (if applicable).
+
+__Service Files__
+
+ * `ang/{mymodule}/{FooBar}.js` - The declaration and logic for a service named `mymoduleFooBar`.
+ * `ang/{mymodule}/{FooBar}.md` - Developer documentation about the service (if applicable).
+
+!!! tip "Tip: Use tilde (`~`) to load HTML templates"
+ When writing code for Angular, you might use an expression like
+ `{templateUrl: 'https://example.org/FooBar.html'}`. However,
+ constructing a full URL that works in every Civi deployment would be
+ complex. Instead, use the tilde prefix. For example, `{templateUrl: '~/mymodule/FooBar.html'}`.
diff --git a/docs/framework/angular/index.md b/docs/framework/angular/index.md
new file mode 100644
index 00000000..d20fb051
--- /dev/null
+++ b/docs/framework/angular/index.md
@@ -0,0 +1,101 @@
+# AngularJS: Overview
+
+AngularJS is a client-side framework for development of rich web
+applications. The core CiviCRM application uses AngularJS for several
+administrative screens, and extensions increasingly use AngularJS for
+"leaps" that add or replace major parts of the application.
+
+This documentation aims to explain how AngularJS works within a CiviCRM
+context.
+
+## Two cultures
+
+CiviCRM is an extensible PHP application (similar to Drupal, Joomla, or
+WordPress). In this culture, the common expectation is that an
+*administrator* installs the main application. To customize it, they
+download, evaluate, and configure a set of business-oriented modules. The
+administrator's workflow is dominated by web-based config screens and CLI
+commands.
+
+AngularJS is a frontend, Javascript development framework. In this culture,
+the expectation is that a *developer* creates a new application. To
+customize it, they download, evaluate, and configure a set of
+function-oriented libraries. The developer's workflow is dominated by CLI's
+and code.
+
+The CiviCRM-AngularJS integration must balance the expectations of these
+two cultures. The balance works as follows:
+
+ * __Build/Activation__: The process of building or activating modules
+ should meet administrators' expectations. It should be managed by the
+ PHP application. (This means that you won't see `gulp` or `grunt`
+ orchestrating the final build -- because PHP logic fills that role.)
+ * __Frontend Code uses Angular (JS+HTML)__: The general structure of the
+ Javascript and HTML files should meet the frontend developers'
+ expectations. These files should be grounded in the same notations and
+ concepts as the upstream AngularJS framework. (This means that AngularJS
+ is not abstracted, wrapped, or mapped by an intermediary like
+ HTML_QuickForm, Symfony Forms or Drupal Form API.)
+ * __Backend Code uses Civi API (PHP)__: The general structure of
+ web-services should meet the backend developers' expectations. These are
+ implemented in PHP (typically with CiviCRM APIv3).
+
+## Basics
+
+AngularJS is a client-side Javascript framework, and it interacts with
+CiviCRM in two major ways. To see this, let's consider an example AngularJS
+page -- it's an HTML document that looks a lot like this:
+
+```html
+
+ 1:
+ 2:
+ 3:
+ 4:
+ 5:
+ 6:
+ 7:
+ 8:
...site wide header...
+ 9:
+10:
...site wide footer...
+11:
+12:
+```
+
+The first interaction comes when CiviCRM generates the initial HTML page:
+
+ * CiviCRM listens for requests to the path `civicrm/a`. (It does this in a
+ way which is compatible with multiple CMSs -- Drupal, Joomla, WordPress, etc.)
+ * CiviCRM builds the list of CSS/JS/JSON resources in lines 3-5. (It does this in a
+ way which allows extensions to add new CSS/JS/JSON. See also:
+ [Resource Reference](https://wiki.civicrm.org/confluence/display/CRMDOC/Resource+Reference).)
+ * CiviCRM ensures that the page includes the site-wide elements, such as
+ lines 8 and 10. (It does this in a way which is compatible with multiple CMSs.)
+
+Once the page is loaded, it works just like any AngularJS 1.x application.
+It uses concepts like `ng-app`, "module", "directive", "service", "component", and
+"partial".
+
+!!! seealso "Read more about AngularJS 1.x"
+ A good resource for understanding AngularJS concepts is [the
+ official AngularJS tutorial](https://code.angularjs.org/1.5.11/docs/tutorial).
+
+The second interaction comes when the AngularJS application loads or stores
+data. This uses the CiviCRM API. Key concepts in CiviCRM API include
+"entity", "action", "params", the "API Explorer", and the bindings for PHP/Javascript/CLI.
+
+!!! seealso "Read more about CiviCRM API"
+ A good resource for understanding CiviCRM API concepts is the [APIv3:
+ Intro](/api/index.md).
+
+In the remainder of this document, we'll try to avoid in-depth discussion
+about the internals of AngularJS 1.x or APIv3. You should be able to follow
+the discussion if you have a beginner-level understanding of both.
+
+
+## Additional pages
+
+ * [Quick Start](quickstart.md): How to create a new screen in the CiviCRM-Angular UI
+ * [File Names](files.md): How AngularJS files are named in `civicrm-core` and `civix`
+ * [Loader](loader.md): How to find and load JS/CSS files for CiviCRM-Angular
+ * [Changesets](changeset.md): How a third-party can modify the content of a CiviCRM-Angular UI
diff --git a/docs/framework/angular/loader.md b/docs/framework/angular/loader.md
new file mode 100644
index 00000000..3ba2473c
--- /dev/null
+++ b/docs/framework/angular/loader.md
@@ -0,0 +1,186 @@
+# AngularJS: Loader
+
+What happens when a user visits a CiviCRM-Angular page? For example, let's
+consider this URL:
+
+ * `https://example.org/civicrm/a/#/caseType`
+
+Broadly speaking, two things happen:
+
+ 1. (Server-side) CiviCRM processes the request for `civicrm/a`. It
+ displays a web-page with all your Angular modules -- such as
+ `ngRoute`, `crmAttachment`, `crmCaseType`, `crmUi`, and so on.
+ 2. (Client-side) AngularJS processes the HTML/JS/CSS. It finds that the
+ module `crmCaseType` includes a route for `#/caseType` and loads the
+ appropriate HTML template.
+
+The client-side behavior is well-defined by Angular
+[ngRoute](https://docs.angularjs.org/api/ngRoute). We'll explore the
+server-side in greater depth because that is unique to the CiviCRM-Angular
+integration.
+
+!!! caution "Caution: Discusses new/experimental interfaces"
+
+ Some elements of this document have been around since CiviCRM v4.6
+ and should remain well-supported. Other elements are new circa v4.7.21
+ and are still flagged experimental.
+
+## Library of modules
+
+The CiviCRM-Angular loader needs a list of available AngularJS modules.
+This list depends on your system configuration (e.g. which CiviCRM
+extensions are enabled). To view the current list, run `cv`:
+
+```
+$ cv ang:module:list
+For a full list, try passing --user=[username].
++-------------------+-------------+------------------------------------------------------------------------------------+
+| name | basePages | requires |
++-------------------+-------------+------------------------------------------------------------------------------------+
+| angularFileUpload | civicrm/a | |
+| bw.paging | (as-needed) | |
+| civicase | (as-needed) | crmUi, crmUtil, ngRoute, angularFileUpload, bw.paging, crmRouteBinder, crmResource |
+| crmApp | civicrm/a | |
+| crmAttachment | civicrm/a | angularFileUpload, crmResource |
+| crmAutosave | civicrm/a | crmUtil |
+| crmCaseType | civicrm/a | ngRoute, ui.utils, crmUi, unsavedChanges, crmUtil, crmResource |
+...snip...
+```
+
+!!! tip "Tip: More options for `cv ang:module:list`"
+ Use `--columns` to specify which columns to display. Ex:
+
+ ```
+ $ cv ang:module:list --columns=name,ext,extDir
+ ```
+
+ Use `--out` to specify an output format. Ex:
+
+ ```
+ $ cv ang:module:list --out=json-pretty
+ ```
+
+ Use `--user` to specify a login user. This may reveal permission-dependent modules. Ex:
+
+ ```
+ $ cv ang:module:list --user=admin
+ ```
+
+Under-the-hood, this library of modules is built via
+[hook_civicrm_angularModules](/hooks/hook_civicrm_angularModules.md), e.g.
+
+```php
+/**
+ * Implements hook_civicrm_angularModules.
+ */
+function foobar_civicrm_angularModules(&$angularModules) {
+ $angularModules['myBigAngularModule'] = array(
+ 'ext' => 'org.example.foobar',
+ 'basePages' => array('civicrm/a'),
+ 'requires' => array('crmUi', 'crmUtils', 'ngRoute'),
+ 'js' => array('ang/myBigangularModule/*.js'),
+ 'css' => array('ang/myBigangularModule/*.css'),
+ 'partials' => array('ang/myBigangularModule'),
+ );
+}
+```
+
+!!! tip "Tip: Generating skeletal code with `civix`"
+ In practice, one usually doesn't need to implement this hook directly.
+ Instead, generate skeletal code with `civix`. For details, see
+ [AngularJS: Quick Start](/framework/angular/quickstart.md).
+
+## Default base-page
+
+CiviCRM includes a "base-page" named `civicrm/a`. By default, this page
+includes the core AngularJS files as well as all the modules in the library.
+
+The page is generated with a PHP class, `AngularLoader`, using logic like this:
+
+```php
+$loader = new \Civi\Angular\AngularLoader();
+$loader->setModules(array('crmApp'));
+$loader->setPageName('civicrm/a');
+$loader->load();
+```
+
+The `load()` function determines the necessary JS/CSS/HTML/JSON resources
+and loads them on the page. This will include:
+
+ * Any AngularJS modules explicitly listed in `setModules(...)`. (Ex: `crmApp`)
+ * Any AngularJS modules with matching `basePages`. (Ex: The value `civicrm/a`
+ is specified by both `setPageName(...)` [above] and `myBigAngularModule` [above].)
+ * Any AngularJS modules transitively required by the above.
+
+!!! note "What makes `civicrm/a` special?"
+ When declaring a module, the property `basePages` will default to
+ `array('civicrm/a')`. In other words, if you don't specify otherwise,
+ all modules are loaded on `civicrm/a`.
+
+!!! note "How does `load()` output the `